From 0732783ed18052cbcc252aef173b8a9f8e9fc9ec Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Thu, 10 Sep 2026 13:48:06 +0800 Subject: [PATCH 1/4] Widen failure group indexes --- ...603_WidenGroupAggregateIndexes.Designer.cs | 918 ++++++++++++++++++ ...260910054603_WidenGroupAggregateIndexes.cs | 56 ++ ...SqlServiceControlDbContextModelSnapshot.cs | 6 +- .../PostgreSqlServiceControlDbContext.cs | 12 + ...547_WidenGroupAggregateIndexes.Designer.cs | 738 ++++++++++++++ ...260910054547_WidenGroupAggregateIndexes.cs | 56 ++ ...verServiceControlDbContextModelSnapshot.cs | 6 +- .../SqlServerServiceControlDbContext.cs | 14 + .../FailedMessageConfiguration.cs | 4 + .../FailedMessageGroupConfiguration.cs | 2 + 10 files changed, 1810 insertions(+), 2 deletions(-) create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs new file mode 100644 index 0000000000..53f9df4a01 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs @@ -0,0 +1,918 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using ServiceControl.Persistence.EFCore.PostgreSql; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + [DbContext(typeof(PostgreSqlServiceControlDbContext))] + [Migration("20260910054603_WidenGroupAggregateIndexes")] + partial class WidenGroupAggregateIndexes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("request_id"); + + b.Property("ArchiveType") + .HasColumnType("integer") + .HasColumnName("archive_type"); + + b.Property("OperationType") + .HasColumnType("integer") + .HasColumnName("operation_type"); + + b.Property("CurrentBatch") + .HasColumnType("integer") + .HasColumnName("current_batch"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_name"); + + b.Property("NumberOfBatches") + .HasColumnType("integer") + .HasColumnName("number_of_batches"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Started") + .HasColumnType("timestamp with time zone") + .HasColumnName("started"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("integer") + .HasColumnName("total_number_of_messages"); + + b.HasKey("RequestId", "ArchiveType", "OperationType") + .HasName("pk_archive_operations"); + + b.HasIndex("Started") + .HasDatabaseName("ix_archive_operations_started"); + + b.ToTable("archive_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Category") + .IsRequired() + .HasColumnType("text") + .HasColumnName("category"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("custom_check_id"); + + b.Property("FailureReason") + .HasColumnType("text") + .HasColumnName("failure_reason"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_host"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("originating_endpoint_host_id"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_name"); + + b.Property("ReportedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("reported_at"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_custom_checks"); + + b.HasIndex("ReportedAt") + .HasDatabaseName("ix_custom_checks_reported_at"); + + b.HasIndex("Status", "ReportedAt") + .HasDatabaseName("ix_custom_checks_status_reported_at"); + + b.ToTable("custom_checks", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.Property("TrackInstances") + .HasColumnType("boolean") + .HasColumnName("track_instances"); + + b.HasKey("Name") + .HasName("pk_endpoint_settings"); + + b.ToTable("endpoint_settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("category"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("event_type"); + + b.Property("RaisedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("raised_at"); + + b.PrimitiveCollection>("RelatedTo") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("related_to"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("pk_event_log_items"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending() + .HasDatabaseName("ix_event_log_items_raised_at_id"); + + b.ToTable("event_log_items", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("dispatch_context_json"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("dispatch_context_type_name"); + + b.HasKey("Id") + .HasName("pk_external_integration_dispatch_requests"); + + b.ToTable("external_integration_dispatch_requests", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("Body") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("body"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("text") + .HasColumnName("exception_info"); + + b.Property("FailedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("failed_at"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_error_imports"); + + b.HasIndex("FailedAt") + .HasDatabaseName("ix_failed_error_imports_failed_at"); + + b.ToTable("failed_error_imports", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("edit_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_edits"); + + b.HasIndex("EditId") + .HasDatabaseName("ix_failed_message_edits_edit_id"); + + b.ToTable("failed_message_edits", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("body_content_type"); + + b.Property("BodySize") + .HasColumnType("integer") + .HasColumnName("body_size"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("BodyText") + .HasColumnType("text") + .HasColumnName("body_text"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("conversation_id"); + + b.Property("ExceptionMessage") + .HasColumnType("text") + .HasColumnName("exception_message"); + + b.Property("ExceptionType") + .HasColumnType("text") + .HasColumnName("exception_type"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("failing_endpoint_address"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_time_of_failure"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("IsSystemMessage") + .HasColumnType("boolean") + .HasColumnName("is_system_message"); + + b.Property("LastAttemptedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_attempted_at"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("LastTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_time_of_failure"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.Property("MessageType") + .HasColumnType("text") + .HasColumnName("message_type"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("integer") + .HasColumnName("number_of_processing_attempts"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_host"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("receiving_endpoint_host_id"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_name"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_host"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("sending_endpoint_host_id"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("StatusChangedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("status_changed_at"); + + b.Property("TimeSent") + .HasColumnType("timestamp with time zone") + .HasColumnName("time_sent"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_messages"); + + b.HasIndex("ConversationId") + .HasDatabaseName("ix_failed_messages_conversation_id"); + + b.HasIndex("FailingEndpointAddress") + .HasDatabaseName("ix_failed_messages_failing_endpoint_address"); + + b.HasIndex("ReceivingEndpointName") + .HasDatabaseName("ix_failed_messages_receiving_endpoint_name"); + + b.HasIndex("StatusChangedAt") + .HasDatabaseName("ix_failed_messages_status_changed_at") + .HasFilter("status IN (2, 4)"); + + b.HasIndex("TimeSent") + .HasDatabaseName("ix_failed_messages_time_sent"); + + b.HasIndex("Status", "LastModified") + .HasDatabaseName("ix_failed_messages_status_last_modified"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.ToTable("failed_messages", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uuid") + .HasColumnName("failed_message_unique_id"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("type"); + + b.HasKey("FailedMessageUniqueId", "GroupId") + .HasName("pk_failed_message_groups"); + + b.HasIndex("GroupId") + .HasDatabaseName("ix_failed_message_groups_group_id"); + + b.HasIndex("Type", "GroupId") + .HasDatabaseName("ix_failed_message_groups_type_group_id"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("failed_message_groups", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.Property("StageAttempts") + .HasColumnType("integer") + .HasColumnName("stage_attempts"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_retries"); + + b.HasIndex("RetryBatchId") + .HasDatabaseName("ix_failed_message_retries_retry_batch_id"); + + b.ToTable("failed_message_retries", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text") + .HasColumnName("comment"); + + b.HasKey("GroupId") + .HasName("pk_group_comments"); + + b.ToTable("group_comments", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("Id") + .HasName("pk_historic_retry_operations"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending() + .HasDatabaseName("ix_historic_retry_operations_completion_time_id"); + + b.ToTable("historic_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("host"); + + b.Property("HostId") + .HasColumnType("uuid") + .HasColumnName("host_id"); + + b.Property("Monitored") + .HasColumnType("boolean") + .HasColumnName("monitored"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_known_endpoints"); + + b.ToTable("known_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.PrimitiveCollection>("EndpointIndicators") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("endpoint_indicators"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("name"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("normalized_sanitized_name"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sanitized_name"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("scope"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("user_indicator"); + + b.HasKey("NormalizedName", "ThroughputSource") + .HasName("pk_licensing_endpoints"); + + b.HasIndex("NormalizedSanitizedName") + .HasDatabaseName("ix_licensing_endpoints_normalized_sanitized_name"); + + b.ToTable("licensing_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.Property("DateUtc") + .HasColumnType("date") + .HasColumnName("date_utc"); + + b.Property("MessageCount") + .HasColumnType("bigint") + .HasColumnName("message_count"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc") + .HasName("pk_licensing_endpoint_throughput"); + + b.HasIndex("DateUtc") + .HasDatabaseName("ix_licensing_endpoint_throughput_date_utc"); + + b.ToTable("licensing_endpoint_throughput", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("from_physical_address"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("to_physical_address"); + + b.HasKey("FromPhysicalAddress") + .HasName("pk_message_redirects"); + + b.ToTable("message_redirects", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Classifier") + .HasColumnType("text") + .HasColumnName("classifier"); + + b.Property("Context") + .HasColumnType("text") + .HasColumnName("context"); + + b.Property("InitialBatchSize") + .HasColumnType("integer") + .HasColumnName("initial_batch_size"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasColumnType("text") + .HasColumnName("initiated_by_name"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("request_id"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("retry_session_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("staging_id"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_retry_batches"); + + b.HasIndex("Status", "RetrySessionId") + .HasDatabaseName("ix_retry_batches_status_retry_session_id"); + + b.ToTable("retry_batches", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.HasKey("Id") + .HasName("pk_retry_batch_now_forwarding"); + + b.ToTable("retry_batch_now_forwarding", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("key"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Key") + .HasName("pk_settings"); + + b.ToTable("settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("message_type"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("transport_address"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("endpoint"); + + b.HasKey("MessageType", "TransportAddress") + .HasName("pk_subscriptions"); + + b.ToTable("subscriptions", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("classifier"); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("RequestId", "RetryType") + .HasName("pk_unacknowledged_retry_operations"); + + b.ToTable("unacknowledged_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_failed_message_groups_failed_messages_failed_message_unique"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_licensing_endpoint_throughput_licensing_endpoints_normalize"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs new file mode 100644 index 0000000000..9d82efed94 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + /// + public partial class WidenGroupAggregateIndexes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_last_modified", + table: "failed_messages"); + + migrationBuilder.DropIndex( + name: "ix_failed_message_groups_type_group_id", + table: "failed_message_groups"); + + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_last_modified", + table: "failed_messages", + columns: new[] { "status", "last_modified" }) + .Annotation("Npgsql:IndexInclude", new[] { "first_time_of_failure", "last_time_of_failure" }); + + migrationBuilder.CreateIndex( + name: "ix_failed_message_groups_type_group_id", + table: "failed_message_groups", + columns: new[] { "type", "group_id" }) + .Annotation("Npgsql:IndexInclude", new[] { "title" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_last_modified", + table: "failed_messages"); + + migrationBuilder.DropIndex( + name: "ix_failed_message_groups_type_group_id", + table: "failed_message_groups"); + + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_last_modified", + table: "failed_messages", + columns: new[] { "status", "last_modified" }); + + migrationBuilder.CreateIndex( + name: "ix_failed_message_groups_type_group_id", + table: "failed_message_groups", + columns: new[] { "type", "group_id" }); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs index af482619d5..e0347ce2c9 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("ProductVersion", "10.0.10") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -440,6 +440,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Status", "LastModified") .HasDatabaseName("ix_failed_messages_status_last_modified"); + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + b.ToTable("failed_messages", (string)null); }); @@ -474,6 +476,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Type", "GroupId") .HasDatabaseName("ix_failed_message_groups_type_group_id"); + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + b.ToTable("failed_message_groups", (string)null); }); diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs index 2e2daa87aa..09c9c36dd5 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs @@ -23,6 +23,18 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(e => e.StatusChangedAt) .HasFilter($"status IN ({(int)FailedMessageStatus.Resolved}, {(int)FailedMessageStatus.Archived})"); + + // Widen the group-aggregate indexes with covering INCLUDE columns so the + // /api/recoverability/groups/ aggregate is index-only (no key lookups / sequential scan with a + // residual status predicate). See missing-indexes.md. The IncludeProperties API is + // provider-specific, so the widening is applied here rather than in the shared configuration. + modelBuilder.Entity() + .HasIndex(e => new { e.Status, e.LastModified }) + .IncludeProperties(nameof(FailedMessageEntity.FirstTimeOfFailure), nameof(FailedMessageEntity.LastTimeOfFailure)); + + modelBuilder.Entity() + .HasIndex(e => new { e.Type, e.GroupId }) + .IncludeProperties(nameof(FailedMessageGroupEntity.Title)); } public override bool IsDuplicateKeyException(DbUpdateException exception) diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs new file mode 100644 index 0000000000..24d793444c --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs @@ -0,0 +1,738 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ServiceControl.Persistence.EFCore.SqlServer; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerServiceControlDbContext))] + [Migration("20260910054547_WidenGroupAggregateIndexes")] + partial class WidenGroupAggregateIndexes + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ArchiveType") + .HasColumnType("int"); + + b.Property("OperationType") + .HasColumnType("int"); + + b.Property("CurrentBatch") + .HasColumnType("int"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("NumberOfBatches") + .HasColumnType("int"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Started") + .HasColumnType("datetime2"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("int"); + + b.HasKey("RequestId", "ArchiveType", "OperationType"); + + b.HasIndex("Started"); + + b.ToTable("ArchiveOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailureReason") + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReportedAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ReportedAt"); + + b.HasIndex("Status", "ReportedAt"); + + b.ToTable("CustomChecks"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("TrackInstances") + .HasColumnType("bit"); + + b.HasKey("Name"); + + b.ToTable("EndpointSettings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RaisedAt") + .HasColumnType("datetime2"); + + b.PrimitiveCollection("RelatedTo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Severity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending(); + + b.ToTable("EventLogItems"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("ExternalIntegrationDispatchRequests"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Body") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailedAt") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("FailedAt"); + + b.ToTable("FailedErrorImports"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("EditId"); + + b.ToTable("FailedMessageEdits"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("BodySize") + .HasColumnType("int"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("BodyText") + .HasColumnType("nvarchar(max)"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ExceptionMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("ExceptionType") + .HasColumnType("nvarchar(max)"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsSystemMessage") + .HasColumnType("bit"); + + b.Property("LastAttemptedAt") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("MessageType") + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("int"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StatusChangedAt") + .HasColumnType("datetime2"); + + b.Property("TimeSent") + .HasColumnType("datetime2"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("ConversationId"); + + b.HasIndex("FailingEndpointAddress"); + + b.HasIndex("ReceivingEndpointName"); + + b.HasIndex("StatusChangedAt") + .HasFilter("[Status] IN (2, 4)"); + + b.HasIndex("TimeSent"); + + b.HasIndex("Status", "LastModified"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.ToTable("FailedMessages"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("FailedMessageUniqueId", "GroupId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Type", "GroupId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("FailedMessageGroups"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.Property("StageAttempts") + .HasColumnType("int"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("RetryBatchId"); + + b.ToTable("FailedMessageRetries"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("GroupId"); + + b.ToTable("GroupComments"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending(); + + b.ToTable("HistoricRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("HostId") + .HasColumnType("uniqueidentifier"); + + b.Property("Monitored") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("KnownEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.PrimitiveCollection("EndpointIndicators") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("NormalizedName", "ThroughputSource"); + + b.HasIndex("NormalizedSanitizedName"); + + b.ToTable("LicensingEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.Property("DateUtc") + .HasColumnType("date"); + + b.Property("MessageCount") + .HasColumnType("bigint"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc"); + + b.HasIndex("DateUtc"); + + b.ToTable("LicensingEndpointThroughput"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("FromPhysicalAddress"); + + b.ToTable("MessageRedirects"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Classifier") + .HasColumnType("nvarchar(max)"); + + b.Property("Context") + .HasColumnType("nvarchar(max)"); + + b.Property("InitialBatchSize") + .HasColumnType("int"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasColumnType("nvarchar(max)"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Status", "RetrySessionId"); + + b.ToTable("RetryBatches"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("RetryBatchNowForwarding"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("MessageType", "TransportAddress"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("RequestId", "RetryType"); + + b.ToTable("UnacknowledgedRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs new file mode 100644 index 0000000000..a41691c147 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + /// + public partial class WidenGroupAggregateIndexes : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_LastModified", + table: "FailedMessages"); + + migrationBuilder.DropIndex( + name: "IX_FailedMessageGroups_Type_GroupId", + table: "FailedMessageGroups"); + + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_LastModified", + table: "FailedMessages", + columns: new[] { "Status", "LastModified" }) + .Annotation("SqlServer:Include", new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + migrationBuilder.CreateIndex( + name: "IX_FailedMessageGroups_Type_GroupId", + table: "FailedMessageGroups", + columns: new[] { "Type", "GroupId" }) + .Annotation("SqlServer:Include", new[] { "Title" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_LastModified", + table: "FailedMessages"); + + migrationBuilder.DropIndex( + name: "IX_FailedMessageGroups_Type_GroupId", + table: "FailedMessageGroups"); + + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_LastModified", + table: "FailedMessages", + columns: new[] { "Status", "LastModified" }); + + migrationBuilder.CreateIndex( + name: "IX_FailedMessageGroups_Type_GroupId", + table: "FailedMessageGroups", + columns: new[] { "Type", "GroupId" }); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs index 27868fadac..c8c5bae03a 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("ProductVersion", "10.0.10") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -350,6 +350,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Status", "LastModified"); + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + b.ToTable("FailedMessages"); }); @@ -377,6 +379,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Type", "GroupId"); + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + b.ToTable("FailedMessageGroups"); }); diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs index 3403cd3c6c..c70343cbd0 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs @@ -21,9 +21,23 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + // Drives the retention sweep. The index is restricted to the statuses the sweep deletes + // (Resolved and Archived) by a provider specific filter, applied in the provider DbContext. modelBuilder.Entity() .HasIndex(e => e.StatusChangedAt) .HasFilter($"[Status] IN ({(int)FailedMessageStatus.Resolved}, {(int)FailedMessageStatus.Archived})"); + + // Widen the group-aggregate indexes with covering INCLUDE columns so the + // /api/recoverability/groups/ aggregate is index-only (no key lookups / clustered scan with a + // residual Status predicate). See missing-indexes.md. The IncludeProperties API is + // provider-specific, so the widening is applied here rather than in the shared configuration. + modelBuilder.Entity() + .HasIndex(e => new { e.Status, e.LastModified }) + .IncludeProperties(nameof(FailedMessageEntity.FirstTimeOfFailure), nameof(FailedMessageEntity.LastTimeOfFailure)); + + modelBuilder.Entity() + .HasIndex(e => new { e.Type, e.GroupId }) + .IncludeProperties(nameof(FailedMessageGroupEntity.Title)); } public override bool IsDuplicateKeyException(DbUpdateException exception) diff --git a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs index 406c842464..272f6c841a 100644 --- a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs @@ -33,6 +33,10 @@ public void Configure(EntityTypeBuilder builder) builder.Property(e => e.BodyStoredExternally).IsRequired(); builder.Property(e => e.BodySize).IsRequired(); + // Drives the group aggregate's MIN/MAX(FirstTimeOfFailure, LastTimeOfFailure) over the + // unresolved set. The FirstTimeOfFailure/LastTimeOfFailure INCLUDE columns are added in the + // provider DbContexts (the IncludeProperties API is provider-specific and is not available in + // this shared project). builder.HasIndex(e => new { e.Status, e.LastModified }); builder.HasIndex(e => e.ReceivingEndpointName); builder.HasIndex(e => e.FailingEndpointAddress); diff --git a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageGroupConfiguration.cs b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageGroupConfiguration.cs index 750de7c3a5..6b47d7c755 100644 --- a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageGroupConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageGroupConfiguration.cs @@ -17,6 +17,8 @@ public void Configure(EntityTypeBuilder builder) builder.HasIndex(e => e.GroupId); // Drives the per-classifier group aggregate, which filters on Type and groups by GroupId. + // The Title INCLUDE column is added in the provider DbContexts (the IncludeProperties API + // is provider-specific and is not available in this shared project). builder.HasIndex(e => new { e.Type, e.GroupId }); builder.HasOne() From 3b359b96c72c1e6fce45a1fb1d9d06b097ac1157 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 11 Sep 2026 09:33:09 +0800 Subject: [PATCH 2/4] Add additional indexes and improve failure group query --- ...ddStatusLastTimeOfFailureIndex.Designer.cs | 921 ++++++++++++++++++ ...1010148_AddStatusLastTimeOfFailureIndex.cs | 27 + ...SqlServiceControlDbContextModelSnapshot.cs | 5 +- .../PostgreSqlServiceControlDbContext.cs | 2 +- ...ddStatusLastTimeOfFailureIndex.Designer.cs | 740 ++++++++++++++ ...1010140_AddStatusLastTimeOfFailureIndex.cs | 27 + ...verServiceControlDbContextModelSnapshot.cs | 4 +- .../SqlServerServiceControlDbContext.cs | 2 +- .../FailedMessageConfiguration.cs | 5 + .../Implementation/GroupsDataStore.cs | 45 +- .../Infrastructure/FailureGroupQueries.cs | 37 + 11 files changed, 1806 insertions(+), 9 deletions(-) create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs new file mode 100644 index 0000000000..67f07b61cd --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs @@ -0,0 +1,921 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using ServiceControl.Persistence.EFCore.PostgreSql; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + [DbContext(typeof(PostgreSqlServiceControlDbContext))] + [Migration("20260911010148_AddStatusLastTimeOfFailureIndex")] + partial class AddStatusLastTimeOfFailureIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("request_id"); + + b.Property("ArchiveType") + .HasColumnType("integer") + .HasColumnName("archive_type"); + + b.Property("OperationType") + .HasColumnType("integer") + .HasColumnName("operation_type"); + + b.Property("CurrentBatch") + .HasColumnType("integer") + .HasColumnName("current_batch"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_name"); + + b.Property("NumberOfBatches") + .HasColumnType("integer") + .HasColumnName("number_of_batches"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Started") + .HasColumnType("timestamp with time zone") + .HasColumnName("started"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("integer") + .HasColumnName("total_number_of_messages"); + + b.HasKey("RequestId", "ArchiveType", "OperationType") + .HasName("pk_archive_operations"); + + b.HasIndex("Started") + .HasDatabaseName("ix_archive_operations_started"); + + b.ToTable("archive_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Category") + .IsRequired() + .HasColumnType("text") + .HasColumnName("category"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("custom_check_id"); + + b.Property("FailureReason") + .HasColumnType("text") + .HasColumnName("failure_reason"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_host"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("originating_endpoint_host_id"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_name"); + + b.Property("ReportedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("reported_at"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_custom_checks"); + + b.HasIndex("ReportedAt") + .HasDatabaseName("ix_custom_checks_reported_at"); + + b.HasIndex("Status", "ReportedAt") + .HasDatabaseName("ix_custom_checks_status_reported_at"); + + b.ToTable("custom_checks", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.Property("TrackInstances") + .HasColumnType("boolean") + .HasColumnName("track_instances"); + + b.HasKey("Name") + .HasName("pk_endpoint_settings"); + + b.ToTable("endpoint_settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("category"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("event_type"); + + b.Property("RaisedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("raised_at"); + + b.PrimitiveCollection>("RelatedTo") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("related_to"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("pk_event_log_items"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending() + .HasDatabaseName("ix_event_log_items_raised_at_id"); + + b.ToTable("event_log_items", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("dispatch_context_json"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("dispatch_context_type_name"); + + b.HasKey("Id") + .HasName("pk_external_integration_dispatch_requests"); + + b.ToTable("external_integration_dispatch_requests", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("Body") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("body"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("text") + .HasColumnName("exception_info"); + + b.Property("FailedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("failed_at"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_error_imports"); + + b.HasIndex("FailedAt") + .HasDatabaseName("ix_failed_error_imports_failed_at"); + + b.ToTable("failed_error_imports", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("edit_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_edits"); + + b.HasIndex("EditId") + .HasDatabaseName("ix_failed_message_edits_edit_id"); + + b.ToTable("failed_message_edits", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("body_content_type"); + + b.Property("BodySize") + .HasColumnType("integer") + .HasColumnName("body_size"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("BodyText") + .HasColumnType("text") + .HasColumnName("body_text"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("conversation_id"); + + b.Property("ExceptionMessage") + .HasColumnType("text") + .HasColumnName("exception_message"); + + b.Property("ExceptionType") + .HasColumnType("text") + .HasColumnName("exception_type"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("failing_endpoint_address"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_time_of_failure"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("IsSystemMessage") + .HasColumnType("boolean") + .HasColumnName("is_system_message"); + + b.Property("LastAttemptedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_attempted_at"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("LastTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_time_of_failure"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.Property("MessageType") + .HasColumnType("text") + .HasColumnName("message_type"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("integer") + .HasColumnName("number_of_processing_attempts"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_host"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("receiving_endpoint_host_id"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_name"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_host"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("sending_endpoint_host_id"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("StatusChangedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("status_changed_at"); + + b.Property("TimeSent") + .HasColumnType("timestamp with time zone") + .HasColumnName("time_sent"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_messages"); + + b.HasIndex("ConversationId") + .HasDatabaseName("ix_failed_messages_conversation_id"); + + b.HasIndex("FailingEndpointAddress") + .HasDatabaseName("ix_failed_messages_failing_endpoint_address"); + + b.HasIndex("ReceivingEndpointName") + .HasDatabaseName("ix_failed_messages_receiving_endpoint_name"); + + b.HasIndex("StatusChangedAt") + .HasDatabaseName("ix_failed_messages_status_changed_at") + .HasFilter("status IN (2, 4)"); + + b.HasIndex("TimeSent") + .HasDatabaseName("ix_failed_messages_time_sent"); + + b.HasIndex("Status", "LastModified") + .HasDatabaseName("ix_failed_messages_status_last_modified"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.HasIndex("Status", "LastTimeOfFailure") + .HasDatabaseName("ix_failed_messages_status_last_time_of_failure"); + + b.ToTable("failed_messages", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uuid") + .HasColumnName("failed_message_unique_id"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("type"); + + b.HasKey("FailedMessageUniqueId", "GroupId") + .HasName("pk_failed_message_groups"); + + b.HasIndex("GroupId") + .HasDatabaseName("ix_failed_message_groups_group_id"); + + b.HasIndex("Type", "GroupId") + .HasDatabaseName("ix_failed_message_groups_type_group_id"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("failed_message_groups", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.Property("StageAttempts") + .HasColumnType("integer") + .HasColumnName("stage_attempts"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_retries"); + + b.HasIndex("RetryBatchId") + .HasDatabaseName("ix_failed_message_retries_retry_batch_id"); + + b.ToTable("failed_message_retries", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text") + .HasColumnName("comment"); + + b.HasKey("GroupId") + .HasName("pk_group_comments"); + + b.ToTable("group_comments", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("Id") + .HasName("pk_historic_retry_operations"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending() + .HasDatabaseName("ix_historic_retry_operations_completion_time_id"); + + b.ToTable("historic_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("host"); + + b.Property("HostId") + .HasColumnType("uuid") + .HasColumnName("host_id"); + + b.Property("Monitored") + .HasColumnType("boolean") + .HasColumnName("monitored"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_known_endpoints"); + + b.ToTable("known_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.PrimitiveCollection>("EndpointIndicators") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("endpoint_indicators"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("name"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("normalized_sanitized_name"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sanitized_name"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("scope"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("user_indicator"); + + b.HasKey("NormalizedName", "ThroughputSource") + .HasName("pk_licensing_endpoints"); + + b.HasIndex("NormalizedSanitizedName") + .HasDatabaseName("ix_licensing_endpoints_normalized_sanitized_name"); + + b.ToTable("licensing_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.Property("DateUtc") + .HasColumnType("date") + .HasColumnName("date_utc"); + + b.Property("MessageCount") + .HasColumnType("bigint") + .HasColumnName("message_count"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc") + .HasName("pk_licensing_endpoint_throughput"); + + b.HasIndex("DateUtc") + .HasDatabaseName("ix_licensing_endpoint_throughput_date_utc"); + + b.ToTable("licensing_endpoint_throughput", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("from_physical_address"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("to_physical_address"); + + b.HasKey("FromPhysicalAddress") + .HasName("pk_message_redirects"); + + b.ToTable("message_redirects", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Classifier") + .HasColumnType("text") + .HasColumnName("classifier"); + + b.Property("Context") + .HasColumnType("text") + .HasColumnName("context"); + + b.Property("InitialBatchSize") + .HasColumnType("integer") + .HasColumnName("initial_batch_size"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasColumnType("text") + .HasColumnName("initiated_by_name"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("request_id"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("retry_session_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("staging_id"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_retry_batches"); + + b.HasIndex("Status", "RetrySessionId") + .HasDatabaseName("ix_retry_batches_status_retry_session_id"); + + b.ToTable("retry_batches", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.HasKey("Id") + .HasName("pk_retry_batch_now_forwarding"); + + b.ToTable("retry_batch_now_forwarding", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("key"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Key") + .HasName("pk_settings"); + + b.ToTable("settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("message_type"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("transport_address"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("endpoint"); + + b.HasKey("MessageType", "TransportAddress") + .HasName("pk_subscriptions"); + + b.ToTable("subscriptions", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("classifier"); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("RequestId", "RetryType") + .HasName("pk_unacknowledged_retry_operations"); + + b.ToTable("unacknowledged_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_failed_message_groups_failed_messages_failed_message_unique"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_licensing_endpoint_throughput_licensing_endpoints_normalize"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs new file mode 100644 index 0000000000..df6e0c1bd5 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + /// + public partial class AddStatusLastTimeOfFailureIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_last_time_of_failure", + table: "failed_messages", + columns: new[] { "status", "last_time_of_failure" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_last_time_of_failure", + table: "failed_messages"); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs index e0347ce2c9..71771fee86 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs @@ -18,7 +18,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("ProductVersion", "10.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -442,6 +442,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + b.HasIndex("Status", "LastTimeOfFailure") + .HasDatabaseName("ix_failed_messages_status_last_time_of_failure"); + b.ToTable("failed_messages", (string)null); }); diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs index 09c9c36dd5..e7d001a2cf 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlServiceControlDbContext.cs @@ -26,7 +26,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) // Widen the group-aggregate indexes with covering INCLUDE columns so the // /api/recoverability/groups/ aggregate is index-only (no key lookups / sequential scan with a - // residual status predicate). See missing-indexes.md. The IncludeProperties API is + // residual status predicate). The IncludeProperties API is // provider-specific, so the widening is applied here rather than in the shared configuration. modelBuilder.Entity() .HasIndex(e => new { e.Status, e.LastModified }) diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs new file mode 100644 index 0000000000..208012a8f1 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs @@ -0,0 +1,740 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ServiceControl.Persistence.EFCore.SqlServer; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerServiceControlDbContext))] + [Migration("20260911010140_AddStatusLastTimeOfFailureIndex")] + partial class AddStatusLastTimeOfFailureIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ArchiveType") + .HasColumnType("int"); + + b.Property("OperationType") + .HasColumnType("int"); + + b.Property("CurrentBatch") + .HasColumnType("int"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("NumberOfBatches") + .HasColumnType("int"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Started") + .HasColumnType("datetime2"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("int"); + + b.HasKey("RequestId", "ArchiveType", "OperationType"); + + b.HasIndex("Started"); + + b.ToTable("ArchiveOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailureReason") + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReportedAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ReportedAt"); + + b.HasIndex("Status", "ReportedAt"); + + b.ToTable("CustomChecks"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("TrackInstances") + .HasColumnType("bit"); + + b.HasKey("Name"); + + b.ToTable("EndpointSettings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RaisedAt") + .HasColumnType("datetime2"); + + b.PrimitiveCollection("RelatedTo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Severity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending(); + + b.ToTable("EventLogItems"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("ExternalIntegrationDispatchRequests"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Body") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailedAt") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("FailedAt"); + + b.ToTable("FailedErrorImports"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("EditId"); + + b.ToTable("FailedMessageEdits"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("BodySize") + .HasColumnType("int"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("BodyText") + .HasColumnType("nvarchar(max)"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ExceptionMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("ExceptionType") + .HasColumnType("nvarchar(max)"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsSystemMessage") + .HasColumnType("bit"); + + b.Property("LastAttemptedAt") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("MessageType") + .HasColumnType("nvarchar(max)"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("int"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StatusChangedAt") + .HasColumnType("datetime2"); + + b.Property("TimeSent") + .HasColumnType("datetime2"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("ConversationId"); + + b.HasIndex("FailingEndpointAddress"); + + b.HasIndex("ReceivingEndpointName"); + + b.HasIndex("StatusChangedAt") + .HasFilter("[Status] IN (2, 4)"); + + b.HasIndex("TimeSent"); + + b.HasIndex("Status", "LastModified"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.HasIndex("Status", "LastTimeOfFailure"); + + b.ToTable("FailedMessages"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("FailedMessageUniqueId", "GroupId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Type", "GroupId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("FailedMessageGroups"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.Property("StageAttempts") + .HasColumnType("int"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("RetryBatchId"); + + b.ToTable("FailedMessageRetries"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("GroupId"); + + b.ToTable("GroupComments"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending(); + + b.ToTable("HistoricRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("HostId") + .HasColumnType("uniqueidentifier"); + + b.Property("Monitored") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("KnownEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.PrimitiveCollection("EndpointIndicators") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("NormalizedName", "ThroughputSource"); + + b.HasIndex("NormalizedSanitizedName"); + + b.ToTable("LicensingEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.Property("DateUtc") + .HasColumnType("date"); + + b.Property("MessageCount") + .HasColumnType("bigint"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc"); + + b.HasIndex("DateUtc"); + + b.ToTable("LicensingEndpointThroughput"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("FromPhysicalAddress"); + + b.ToTable("MessageRedirects"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Classifier") + .HasColumnType("nvarchar(max)"); + + b.Property("Context") + .HasColumnType("nvarchar(max)"); + + b.Property("InitialBatchSize") + .HasColumnType("int"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasColumnType("nvarchar(max)"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Status", "RetrySessionId"); + + b.ToTable("RetryBatches"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("RetryBatchNowForwarding"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("MessageType", "TransportAddress"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("RequestId", "RetryType"); + + b.ToTable("UnacknowledgedRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs new file mode 100644 index 0000000000..fcaa5cfcdc --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs @@ -0,0 +1,27 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + /// + public partial class AddStatusLastTimeOfFailureIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_LastTimeOfFailure", + table: "FailedMessages", + columns: new[] { "Status", "LastTimeOfFailure" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_LastTimeOfFailure", + table: "FailedMessages"); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs index c8c5bae03a..d61695b7b8 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") + .HasAnnotation("ProductVersion", "10.0.11") .HasAnnotation("Relational:MaxIdentifierLength", 128); SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); @@ -352,6 +352,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + b.HasIndex("Status", "LastTimeOfFailure"); + b.ToTable("FailedMessages"); }); diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs index c70343cbd0..1c415c20ff 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/SqlServerServiceControlDbContext.cs @@ -29,7 +29,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) // Widen the group-aggregate indexes with covering INCLUDE columns so the // /api/recoverability/groups/ aggregate is index-only (no key lookups / clustered scan with a - // residual Status predicate). See missing-indexes.md. The IncludeProperties API is + // residual Status predicate). The IncludeProperties API is // provider-specific, so the widening is applied here rather than in the shared configuration. modelBuilder.Entity() .HasIndex(e => new { e.Status, e.LastModified }) diff --git a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs index 272f6c841a..00d73ead8c 100644 --- a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs @@ -38,6 +38,11 @@ public void Configure(EntityTypeBuilder builder) // provider DbContexts (the IncludeProperties API is provider-specific and is not available in // this shared project). builder.HasIndex(e => new { e.Status, e.LastModified }); + + // Serves the failed-messages page sorted by time_of_failure (ServicePulse default sort). + // Keyed (Status, LastTimeOfFailure) so the page query streams instead of scanning the + // clustered table. + builder.HasIndex(e => new { e.Status, e.LastTimeOfFailure }); builder.HasIndex(e => e.ReceivingEndpointName); builder.HasIndex(e => e.FailingEndpointAddress); builder.HasIndex(e => e.ConversationId); diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs index 3ae249c7a1..ad5d305d74 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/GroupsDataStore.cs @@ -22,7 +22,7 @@ public Task> GetUnresolvedGroupsByClassifier(string clas groups = groups.Where(group => group.Title == classifierFilter); } - var views = await MostRecent(groups.AggregateGroups(WithStatus(dbContext, FailedMessageStatus.Unresolved)), token); + var views = await GetGroupViews(groups, WithStatus(dbContext, FailedMessageStatus.Unresolved), token); await AttachComments(dbContext, views, token); @@ -34,7 +34,7 @@ public Task>> GetArchivedGroupsByClassifier( { var groups = ByClassifier(dbContext, classifier); - var views = await MostRecent(groups.AggregateGroups(WithStatus(dbContext, FailedMessageStatus.Archived)), token); + var views = await GetGroupViews(groups, WithStatus(dbContext, FailedMessageStatus.Archived), token); return new QueryResult>(views, views.ToQueryStatsInfo("groups", views.Count)); }, cancellationToken); @@ -127,9 +127,44 @@ static async Task AttachComments(ServiceControlDbContext dbContext, IList> MostRecent(IQueryable groups, CancellationToken cancellationToken) => - await groups - .OrderByDescending(group => group.Last) + /// + /// Two-step group aggregate: + /// 1. Narrow aggregate on (GroupId, Type) — avoids hashing nvarchar(max) Title per joined row. + /// Order by MAX(LastTimeOfFailure) desc, take MaxGroups (200), materialise. + /// 2. Fetch Title per output group (≤200 index seeks) from the same filtered group rows. + /// Title is functionally dependent on GroupId, so the result is identical to grouping by Title. + /// + static async Task> GetGroupViews(IQueryable groups, IQueryable messages, CancellationToken cancellationToken) + { + var summaries = await groups + .AggregateGroupSummaries(messages) + .OrderByDescending(summary => summary.Last) .Take(FailureGroupQueries.MaxGroups) .ToListAsync(cancellationToken); + + if (summaries.Count == 0) + { + return []; + } + + var groupIds = summaries.Select(summary => summary.Id).ToArray(); + + var titles = await groups + .Where(group => groupIds.Contains(group.GroupId)) + .Select(group => new { group.GroupId, group.Title }) + .Distinct() + .ToDictionaryAsync(group => group.GroupId, group => group.Title, cancellationToken); + + return summaries + .Select(summary => new FailureGroupView + { + Id = summary.Id, + Title = titles.GetValueOrDefault(summary.Id) ?? string.Empty, + Type = summary.Type, + Count = summary.Count, + First = summary.First, + Last = summary.Last + }) + .ToList(); + } } diff --git a/src/ServiceControl.Persistence.EFCore/Infrastructure/FailureGroupQueries.cs b/src/ServiceControl.Persistence.EFCore/Infrastructure/FailureGroupQueries.cs index 3c21c23ece..0c31a96178 100644 --- a/src/ServiceControl.Persistence.EFCore/Infrastructure/FailureGroupQueries.cs +++ b/src/ServiceControl.Persistence.EFCore/Infrastructure/FailureGroupQueries.cs @@ -7,6 +7,10 @@ static class FailureGroupQueries { public const int MaxGroups = 200; + /// + /// Aggregate with Title in the group key. Used by + /// where a single group is fetched and the nvarchar(max) Title cost is negligible. + /// public static IQueryable AggregateGroups(this IQueryable groups, IQueryable messages) => from failureGroup in groups join message in messages on failureGroup.FailedMessageUniqueId equals message.UniqueMessageId @@ -21,4 +25,37 @@ into aggregate First = aggregate.Min(message => message.FirstTimeOfFailure), Last = aggregate.Max(message => message.LastTimeOfFailure) }; + + /// + /// Narrow aggregate on (GroupId, Type) only — the first step of the two-step group query. + /// Title is functionally dependent on GroupId (GroupId = DeterministicGuid.MakeId(classifier.Name, + /// classification)), so grouping without it is semantically equivalent. Keeping nvarchar(max) Title + /// out of the group key avoids per-row LOB hashing/sorting and the large memory grant it demands. + /// + public static IQueryable AggregateGroupSummaries(this IQueryable groups, IQueryable messages) => + from failureGroup in groups + join message in messages on failureGroup.FailedMessageUniqueId equals message.UniqueMessageId + group message by new { failureGroup.GroupId, failureGroup.Type } + into aggregate + select new GroupSummary + { + Id = aggregate.Key.GroupId, + Type = aggregate.Key.Type, + Count = aggregate.Count(), + First = aggregate.Min(message => message.FirstTimeOfFailure), + Last = aggregate.Max(message => message.LastTimeOfFailure) + }; +} + +/// +/// Intermediate projection for step 1 of the two-step group aggregate. Title is fetched +/// separately in step 2 to keep nvarchar(max) out of the aggregate hash/sort. +/// +sealed class GroupSummary +{ + public string Id { get; set; } = null!; + public string Type { get; set; } = null!; + public int Count { get; set; } + public DateTime First { get; set; } + public DateTime Last { get; set; } } From f98d15a013e13af77fefc6a70557cd1c149de0f3 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 11 Sep 2026 11:21:37 +0800 Subject: [PATCH 3/4] Add index to message type column for sorting --- ...1949_AddStatusMessageTypeIndex.Designer.cs | 925 ++++++++++++++++++ ...0260911031949_AddStatusMessageTypeIndex.cs | 50 + ...SqlServiceControlDbContextModelSnapshot.cs | 6 +- ...1929_AddStatusMessageTypeIndex.Designer.cs | 743 ++++++++++++++ ...0260911031929_AddStatusMessageTypeIndex.cs | 50 + ...verServiceControlDbContextModelSnapshot.cs | 5 +- .../FailedMessageConfiguration.cs | 12 + .../EFRecoverabilityIngestionUnitOfWork.cs | 12 +- .../EFCore/ErrorIngestionTests.cs | 20 + 9 files changed, 1820 insertions(+), 3 deletions(-) create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs create mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs new file mode 100644 index 0000000000..c08897f124 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs @@ -0,0 +1,925 @@ +// +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using ServiceControl.Persistence.EFCore.PostgreSql; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + [DbContext(typeof(PostgreSqlServiceControlDbContext))] + [Migration("20260911031949_AddStatusMessageTypeIndex")] + partial class AddStatusMessageTypeIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("request_id"); + + b.Property("ArchiveType") + .HasColumnType("integer") + .HasColumnName("archive_type"); + + b.Property("OperationType") + .HasColumnType("integer") + .HasColumnName("operation_type"); + + b.Property("CurrentBatch") + .HasColumnType("integer") + .HasColumnName("current_batch"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("group_name"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_name"); + + b.Property("NumberOfBatches") + .HasColumnType("integer") + .HasColumnName("number_of_batches"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Started") + .HasColumnType("timestamp with time zone") + .HasColumnName("started"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("integer") + .HasColumnName("total_number_of_messages"); + + b.HasKey("RequestId", "ArchiveType", "OperationType") + .HasName("pk_archive_operations"); + + b.HasIndex("Started") + .HasDatabaseName("ix_archive_operations_started"); + + b.ToTable("archive_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Category") + .IsRequired() + .HasColumnType("text") + .HasColumnName("category"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("custom_check_id"); + + b.Property("FailureReason") + .HasColumnType("text") + .HasColumnName("failure_reason"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_host"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("originating_endpoint_host_id"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("originating_endpoint_name"); + + b.Property("ReportedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("reported_at"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_custom_checks"); + + b.HasIndex("ReportedAt") + .HasDatabaseName("ix_custom_checks_reported_at"); + + b.HasIndex("Status", "ReportedAt") + .HasDatabaseName("ix_custom_checks_status_reported_at"); + + b.ToTable("custom_checks", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.Property("TrackInstances") + .HasColumnType("boolean") + .HasColumnName("track_instances"); + + b.HasKey("Name") + .HasName("pk_endpoint_settings"); + + b.ToTable("endpoint_settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("category"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text") + .HasColumnName("description"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("event_type"); + + b.Property("RaisedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("raised_at"); + + b.PrimitiveCollection>("RelatedTo") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("related_to"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("pk_event_log_items"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending() + .HasDatabaseName("ix_event_log_items_raised_at_id"); + + b.ToTable("event_log_items", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("dispatch_context_json"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("dispatch_context_type_name"); + + b.HasKey("Id") + .HasName("pk_external_integration_dispatch_requests"); + + b.ToTable("external_integration_dispatch_requests", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("Body") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("body"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("text") + .HasColumnName("exception_info"); + + b.Property("FailedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("failed_at"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_error_imports"); + + b.HasIndex("FailedAt") + .HasDatabaseName("ix_failed_error_imports_failed_at"); + + b.ToTable("failed_error_imports", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("edit_id"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_edits"); + + b.HasIndex("EditId") + .HasDatabaseName("ix_failed_message_edits_edit_id"); + + b.ToTable("failed_message_edits", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("body_content_type"); + + b.Property("BodySize") + .HasColumnType("integer") + .HasColumnName("body_size"); + + b.Property("BodyStoredExternally") + .HasColumnType("boolean") + .HasColumnName("body_stored_externally"); + + b.Property("BodyText") + .HasColumnType("text") + .HasColumnName("body_text"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("conversation_id"); + + b.Property("ExceptionMessage") + .HasColumnType("text") + .HasColumnName("exception_message"); + + b.Property("ExceptionType") + .HasColumnType("text") + .HasColumnName("exception_type"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("failing_endpoint_address"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_time_of_failure"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("text") + .HasColumnName("headers_json"); + + b.Property("IsSystemMessage") + .HasColumnType("boolean") + .HasColumnName("is_system_message"); + + b.Property("LastAttemptedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_attempted_at"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("LastTimeOfFailure") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_time_of_failure"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_id"); + + b.Property("MessageType") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("message_type"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("integer") + .HasColumnName("number_of_processing_attempts"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_host"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("receiving_endpoint_host_id"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("receiving_endpoint_name"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_host"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uuid") + .HasColumnName("sending_endpoint_host_id"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sending_endpoint_name"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.Property("StatusChangedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("status_changed_at"); + + b.Property("TimeSent") + .HasColumnType("timestamp with time zone") + .HasColumnName("time_sent"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_messages"); + + b.HasIndex("ConversationId") + .HasDatabaseName("ix_failed_messages_conversation_id"); + + b.HasIndex("FailingEndpointAddress") + .HasDatabaseName("ix_failed_messages_failing_endpoint_address"); + + b.HasIndex("ReceivingEndpointName") + .HasDatabaseName("ix_failed_messages_receiving_endpoint_name"); + + b.HasIndex("StatusChangedAt") + .HasDatabaseName("ix_failed_messages_status_changed_at") + .HasFilter("status IN (2, 4)"); + + b.HasIndex("TimeSent") + .HasDatabaseName("ix_failed_messages_time_sent"); + + b.HasIndex("Status", "LastModified") + .HasDatabaseName("ix_failed_messages_status_last_modified"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.HasIndex("Status", "LastTimeOfFailure") + .HasDatabaseName("ix_failed_messages_status_last_time_of_failure"); + + b.HasIndex("Status", "MessageType", "UniqueMessageId") + .HasDatabaseName("ix_failed_messages_status_message_type_unique_message_id"); + + b.ToTable("failed_messages", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uuid") + .HasColumnName("failed_message_unique_id"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("character varying(255)") + .HasColumnName("type"); + + b.HasKey("FailedMessageUniqueId", "GroupId") + .HasName("pk_failed_message_groups"); + + b.HasIndex("GroupId") + .HasDatabaseName("ix_failed_message_groups_group_id"); + + b.HasIndex("Type", "GroupId") + .HasDatabaseName("ix_failed_message_groups_type_group_id"); + + NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("failed_message_groups", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uuid") + .HasColumnName("unique_message_id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.Property("StageAttempts") + .HasColumnType("integer") + .HasColumnName("stage_attempts"); + + b.HasKey("UniqueMessageId") + .HasName("pk_failed_message_retries"); + + b.HasIndex("RetryBatchId") + .HasDatabaseName("ix_failed_message_retries_retry_batch_id"); + + b.ToTable("failed_message_retries", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("group_id"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text") + .HasColumnName("comment"); + + b.HasKey("GroupId") + .HasName("pk_group_comments"); + + b.ToTable("group_comments", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("Id") + .HasName("pk_historic_retry_operations"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending() + .HasDatabaseName("ix_historic_retry_operations_completion_time_id"); + + b.ToTable("historic_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("host"); + + b.Property("HostId") + .HasColumnType("uuid") + .HasColumnName("host_id"); + + b.Property("Monitored") + .HasColumnType("boolean") + .HasColumnName("monitored"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("pk_known_endpoints"); + + b.ToTable("known_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.PrimitiveCollection>("EndpointIndicators") + .IsRequired() + .HasColumnType("text[]") + .HasColumnName("endpoint_indicators"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("name"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("normalized_sanitized_name"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("sanitized_name"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("scope"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("user_indicator"); + + b.HasKey("NormalizedName", "ThroughputSource") + .HasName("pk_licensing_endpoints"); + + b.HasIndex("NormalizedSanitizedName") + .HasDatabaseName("ix_licensing_endpoints_normalized_sanitized_name"); + + b.ToTable("licensing_endpoints", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("character varying(300)") + .HasColumnName("normalized_name"); + + b.Property("ThroughputSource") + .HasColumnType("integer") + .HasColumnName("throughput_source"); + + b.Property("DateUtc") + .HasColumnType("date") + .HasColumnName("date_utc"); + + b.Property("MessageCount") + .HasColumnType("bigint") + .HasColumnName("message_count"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc") + .HasName("pk_licensing_endpoint_throughput"); + + b.HasIndex("DateUtc") + .HasDatabaseName("ix_licensing_endpoint_throughput_date_utc"); + + b.ToTable("licensing_endpoint_throughput", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("from_physical_address"); + + b.Property("LastModified") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_modified"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("to_physical_address"); + + b.HasKey("FromPhysicalAddress") + .HasName("pk_message_redirects"); + + b.ToTable("message_redirects", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uuid") + .HasColumnName("id"); + + b.Property("Classifier") + .HasColumnType("text") + .HasColumnName("classifier"); + + b.Property("Context") + .HasColumnType("text") + .HasColumnName("context"); + + b.Property("InitialBatchSize") + .HasColumnType("integer") + .HasColumnName("initial_batch_size"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("initiated_by_id"); + + b.Property("InitiatedByName") + .HasColumnType("text") + .HasColumnName("initiated_by_name"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("operation_id"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("request_id"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("retry_session_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("staging_id"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.Property("Status") + .HasColumnType("integer") + .HasColumnName("status"); + + b.HasKey("Id") + .HasName("pk_retry_batches"); + + b.HasIndex("Status", "RetrySessionId") + .HasDatabaseName("ix_retry_batches_status_retry_session_id"); + + b.ToTable("retry_batches", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("id"); + + b.Property("RetryBatchId") + .HasColumnType("uuid") + .HasColumnName("retry_batch_id"); + + b.HasKey("Id") + .HasName("pk_retry_batch_now_forwarding"); + + b.ToTable("retry_batch_now_forwarding", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("key"); + + b.Property("Value") + .IsRequired() + .HasColumnType("text") + .HasColumnName("value"); + + b.HasKey("Key") + .HasName("pk_settings"); + + b.ToTable("settings", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("message_type"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("character varying(200)") + .HasColumnName("transport_address"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("endpoint"); + + b.HasKey("MessageType", "TransportAddress") + .HasName("pk_subscriptions"); + + b.ToTable("subscriptions", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("character varying(400)") + .HasColumnName("request_id"); + + b.Property("RetryType") + .HasColumnType("integer") + .HasColumnName("retry_type"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("character varying(450)") + .HasColumnName("classifier"); + + b.Property("CompletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("completion_time"); + + b.Property("Failed") + .HasColumnType("boolean") + .HasColumnName("failed"); + + b.Property("Last") + .HasColumnType("timestamp with time zone") + .HasColumnName("last"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("integer") + .HasColumnName("number_of_messages_processed"); + + b.Property("Originator") + .HasColumnType("text") + .HasColumnName("originator"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_time"); + + b.HasKey("RequestId", "RetryType") + .HasName("pk_unacknowledged_retry_operations"); + + b.ToTable("unacknowledged_retry_operations", (string)null); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_failed_message_groups_failed_messages_failed_message_unique"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_licensing_endpoint_throughput_licensing_endpoints_normalize"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs new file mode 100644 index 0000000000..5833b84aa4 --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations +{ + /// + public partial class AddStatusMessageTypeIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "message_type", + table: "failed_messages", + type: "character varying(450)", + maxLength: 450, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + //normally this would be potentially slow and necessitate executing outside of the + //migration transaction, but no customers consume this persister yet so this can + //treated as an initial migration + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_message_type_unique_message_id", + table: "failed_messages", + columns: new[] { "status", "message_type", "unique_message_id" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_message_type_unique_message_id", + table: "failed_messages"); + + migrationBuilder.AlterColumn( + name: "message_type", + table: "failed_messages", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(450)", + oldMaxLength: 450, + oldNullable: true); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs index 71771fee86..b32d5ae3fa 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs @@ -371,7 +371,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnName("message_id"); b.Property("MessageType") - .HasColumnType("text") + .HasMaxLength(450) + .HasColumnType("character varying(450)") .HasColumnName("message_type"); b.Property("NumberOfProcessingAttempts") @@ -445,6 +446,9 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Status", "LastTimeOfFailure") .HasDatabaseName("ix_failed_messages_status_last_time_of_failure"); + b.HasIndex("Status", "MessageType", "UniqueMessageId") + .HasDatabaseName("ix_failed_messages_status_message_type_unique_message_id"); + b.ToTable("failed_messages", (string)null); }); diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs new file mode 100644 index 0000000000..aa505f5e4f --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs @@ -0,0 +1,743 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ServiceControl.Persistence.EFCore.SqlServer; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + [DbContext(typeof(SqlServerServiceControlDbContext))] + [Migration("20260911031929_AddStatusMessageTypeIndex")] + partial class AddStatusMessageTypeIndex + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.11") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("ArchiveType") + .HasColumnType("int"); + + b.Property("OperationType") + .HasColumnType("int"); + + b.Property("CurrentBatch") + .HasColumnType("int"); + + b.Property("GroupName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("NumberOfBatches") + .HasColumnType("int"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Started") + .HasColumnType("datetime2"); + + b.Property("TotalNumberOfMessages") + .HasColumnType("int"); + + b.HasKey("RequestId", "ArchiveType", "OperationType"); + + b.HasIndex("Started"); + + b.ToTable("ArchiveOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Category") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("CustomCheckId") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailureReason") + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHost") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("OriginatingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("OriginatingEndpointName") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("ReportedAt") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("ReportedAt"); + + b.HasIndex("Status", "ReportedAt"); + + b.ToTable("CustomChecks"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => + { + b.Property("Name") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("TrackInstances") + .HasColumnType("bit"); + + b.HasKey("Name"); + + b.ToTable("EndpointSettings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Category") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Description") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("EventType") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RaisedAt") + .HasColumnType("datetime2"); + + b.PrimitiveCollection("RelatedTo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Severity") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("RaisedAt", "Id") + .IsDescending(); + + b.ToTable("EventLogItems"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DispatchContextJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("DispatchContextTypeName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("ExternalIntegrationDispatchRequests"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("Body") + .IsRequired() + .HasColumnType("varbinary(max)"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("ExceptionInfo") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("FailedAt") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("MessageId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("FailedAt"); + + b.ToTable("FailedErrorImports"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("EditId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("EditId"); + + b.ToTable("FailedMessageEdits"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("BodyContentType") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("BodySize") + .HasColumnType("int"); + + b.Property("BodyStoredExternally") + .HasColumnType("bit"); + + b.Property("BodyText") + .HasColumnType("nvarchar(max)"); + + b.Property("ConversationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ExceptionMessage") + .HasColumnType("nvarchar(max)"); + + b.Property("ExceptionType") + .HasColumnType("nvarchar(max)"); + + b.Property("FailingEndpointAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("FirstTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("HeadersJson") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("IsSystemMessage") + .HasColumnType("bit"); + + b.Property("LastAttemptedAt") + .HasColumnType("datetime2"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("LastTimeOfFailure") + .HasColumnType("datetime2"); + + b.Property("MessageId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("MessageType") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("NumberOfProcessingAttempts") + .HasColumnType("int"); + + b.Property("ReceivingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("ReceivingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("ReceivingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHost") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SendingEndpointHostId") + .HasColumnType("uniqueidentifier"); + + b.Property("SendingEndpointName") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Status") + .HasColumnType("int"); + + b.Property("StatusChangedAt") + .HasColumnType("datetime2"); + + b.Property("TimeSent") + .HasColumnType("datetime2"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("ConversationId"); + + b.HasIndex("FailingEndpointAddress"); + + b.HasIndex("ReceivingEndpointName"); + + b.HasIndex("StatusChangedAt") + .HasFilter("[Status] IN (2, 4)"); + + b.HasIndex("TimeSent"); + + b.HasIndex("Status", "LastModified"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + + b.HasIndex("Status", "LastTimeOfFailure"); + + b.HasIndex("Status", "MessageType", "UniqueMessageId"); + + b.ToTable("FailedMessages"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.Property("FailedMessageUniqueId") + .HasColumnType("uniqueidentifier"); + + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Title") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)"); + + b.HasKey("FailedMessageUniqueId", "GroupId"); + + b.HasIndex("GroupId"); + + b.HasIndex("Type", "GroupId"); + + SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); + + b.ToTable("FailedMessageGroups"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => + { + b.Property("UniqueMessageId") + .HasColumnType("uniqueidentifier"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.Property("StageAttempts") + .HasColumnType("int"); + + b.HasKey("UniqueMessageId"); + + b.HasIndex("RetryBatchId"); + + b.ToTable("FailedMessageRetries"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => + { + b.Property("GroupId") + .HasMaxLength(64) + .HasColumnType("nvarchar(64)"); + + b.Property("Comment") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("GroupId"); + + b.ToTable("GroupComments"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("Id"); + + b.HasIndex("CompletionTime", "Id") + .IsDescending(); + + b.ToTable("HistoricRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Host") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("HostId") + .HasColumnType("uniqueidentifier"); + + b.Property("Monitored") + .HasColumnType("bit"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.ToTable("KnownEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.PrimitiveCollection("EndpointIndicators") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("NormalizedSanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("SanitizedName") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Scope") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("UserIndicator") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("NormalizedName", "ThroughputSource"); + + b.HasIndex("NormalizedSanitizedName"); + + b.ToTable("LicensingEndpoints"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.Property("NormalizedName") + .HasMaxLength(300) + .HasColumnType("nvarchar(300)"); + + b.Property("ThroughputSource") + .HasColumnType("int"); + + b.Property("DateUtc") + .HasColumnType("date"); + + b.Property("MessageCount") + .HasColumnType("bigint"); + + b.HasKey("NormalizedName", "ThroughputSource", "DateUtc"); + + b.HasIndex("DateUtc"); + + b.ToTable("LicensingEndpointThroughput"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => + { + b.Property("FromPhysicalAddress") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("LastModified") + .HasColumnType("datetime2"); + + b.Property("ToPhysicalAddress") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("FromPhysicalAddress"); + + b.ToTable("MessageRedirects"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => + { + b.Property("Id") + .HasColumnType("uniqueidentifier"); + + b.Property("Classifier") + .HasColumnType("nvarchar(max)"); + + b.Property("Context") + .HasColumnType("nvarchar(max)"); + + b.Property("InitialBatchSize") + .HasColumnType("int"); + + b.Property("InitiatedById") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("InitiatedByName") + .HasColumnType("nvarchar(max)"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("OperationId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("RequestId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetrySessionId") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("StagingId") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.Property("Status") + .HasColumnType("int"); + + b.HasKey("Id"); + + b.HasIndex("Status", "RetrySessionId"); + + b.ToTable("RetryBatches"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => + { + b.Property("Id") + .HasColumnType("int"); + + b.Property("RetryBatchId") + .HasColumnType("uniqueidentifier"); + + b.HasKey("Id"); + + b.ToTable("RetryBatchNowForwarding"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => + { + b.Property("Key") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("Value") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.HasKey("Key"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => + { + b.Property("MessageType") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("TransportAddress") + .HasMaxLength(200) + .HasColumnType("nvarchar(200)"); + + b.Property("Endpoint") + .IsRequired() + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.HasKey("MessageType", "TransportAddress"); + + b.ToTable("Subscriptions"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => + { + b.Property("RequestId") + .HasMaxLength(400) + .HasColumnType("nvarchar(400)"); + + b.Property("RetryType") + .HasColumnType("int"); + + b.Property("Classifier") + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); + + b.Property("CompletionTime") + .HasColumnType("datetime2"); + + b.Property("Failed") + .HasColumnType("bit"); + + b.Property("Last") + .HasColumnType("datetime2"); + + b.Property("NumberOfMessagesProcessed") + .HasColumnType("int"); + + b.Property("Originator") + .HasColumnType("nvarchar(max)"); + + b.Property("StartTime") + .HasColumnType("datetime2"); + + b.HasKey("RequestId", "RetryType"); + + b.ToTable("UnacknowledgedRetryOperations"); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) + .WithMany() + .HasForeignKey("FailedMessageUniqueId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => + { + b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) + .WithMany() + .HasForeignKey("NormalizedName", "ThroughputSource") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs new file mode 100644 index 0000000000..bf1edddb9c --- /dev/null +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs @@ -0,0 +1,50 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations +{ + /// + public partial class AddStatusMessageTypeIndex : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "MessageType", + table: "FailedMessages", + type: "nvarchar(450)", + maxLength: 450, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + + //normally this would be potentially slow and necessitate executing outside of the + //migration transaction, but no customers consume this persister yet so this can + //treated as an initial migration + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", + table: "FailedMessages", + columns: new[] { "Status", "MessageType", "UniqueMessageId" }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", + table: "FailedMessages"); + + migrationBuilder.AlterColumn( + name: "MessageType", + table: "FailedMessages", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(450)", + oldMaxLength: 450, + oldNullable: true); + } + } +} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs index d61695b7b8..b9e41a3681 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/SqlServerServiceControlDbContextModelSnapshot.cs @@ -299,7 +299,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("nvarchar(450)"); b.Property("MessageType") - .HasColumnType("nvarchar(max)"); + .HasMaxLength(450) + .HasColumnType("nvarchar(450)"); b.Property("NumberOfProcessingAttempts") .HasColumnType("int"); @@ -354,6 +355,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("Status", "LastTimeOfFailure"); + b.HasIndex("Status", "MessageType", "UniqueMessageId"); + b.ToTable("FailedMessages"); }); diff --git a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs index 00d73ead8c..d143d4973b 100644 --- a/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs +++ b/src/ServiceControl.Persistence.EFCore/EntityConfigurations/FailedMessageConfiguration.cs @@ -20,6 +20,11 @@ public void Configure(EntityTypeBuilder builder) builder.Property(e => e.LastAttemptedAt).IsRequired(); builder.Property(e => e.MessageId).HasMaxLength(ColumnLengths.ShortTextLength); + // 450 and not nvarchar(max): the column has to be indexable to serve sort=message_type, and + // SQL Server rejects nvarchar(max) as an index key column. Type names are short-by-nature + // (the enricher stores the first comma token of EnclosedMessageTypes); ingestion enforces + // the cap so the write path can never fail on a longer value. + builder.Property(e => e.MessageType).HasMaxLength(ColumnLengths.ShortTextLength); builder.Property(e => e.ConversationId).HasMaxLength(ColumnLengths.ShortTextLength); builder.Property(e => e.SendingEndpointName).HasMaxLength(ColumnLengths.ShortTextLength); builder.Property(e => e.SendingEndpointHost).HasMaxLength(ColumnLengths.ShortTextLength); @@ -43,6 +48,13 @@ public void Configure(EntityTypeBuilder builder) // Keyed (Status, LastTimeOfFailure) so the page query streams instead of scanning the // clustered table. builder.HasIndex(e => new { e.Status, e.LastTimeOfFailure }); + + // Serves the failed-messages page sorted by message_type. UniqueMessageId is an explicit + // key column and not just the SQL Server row locator: a message type repeats across many + // failed messages, and the page query's tie-break (ORDER BY MessageType DESC, + // UniqueMessageId DESC) has to come from the index itself on providers without a row + // locator concept (PostgreSQL), or the sort degrades to sorting every tie group. + builder.HasIndex(e => new { e.Status, e.MessageType, e.UniqueMessageId }); builder.HasIndex(e => e.ReceivingEndpointName); builder.HasIndex(e => e.FailingEndpointAddress); builder.HasIndex(e => e.ConversationId); diff --git a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs index 03739fbad0..762ecbb7fe 100644 --- a/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs +++ b/src/ServiceControl.Persistence.EFCore/Implementation/UnitOfWork/EFRecoverabilityIngestionUnitOfWork.cs @@ -6,6 +6,7 @@ namespace ServiceControl.Persistence.EFCore.Implementation.UnitOfWork; using ServiceControl.MessageFailures; using ServiceControl.Operations; using ServiceControl.Persistence.EFCore.Abstractions; +using ServiceControl.Persistence.EFCore.EntityConfigurations; using ServiceControl.Persistence.EFCore.Infrastructure; using ServiceControl.Persistence.Infrastructure; using ServiceControl.Persistence.UnitOfWork; @@ -39,7 +40,7 @@ public Task RecordFailedProcessingAttempt(MessageContext context, Groups = groups, HeadersJson = MessageHeaders.Write(processingAttempt.Headers), MessageId = processingAttempt.MessageId, - MessageType = GetMetadata(processingAttempt, "MessageType"), + MessageType = TruncateForColumn(GetMetadata(processingAttempt, "MessageType")), TimeSent = GetMetadata(processingAttempt, "TimeSent"), ConversationId = GetMetadata(processingAttempt, "ConversationId"), SendingEndpointName = sendingEndpoint?.Name, @@ -68,6 +69,15 @@ public Task RecordSuccessfulRetry(string retriedMessageUniqueId, DateTime succee return Task.CompletedTask; } + // The MessageType column is length-bounded (ColumnLengths.ShortTextLength) so that it can be + // an index key serving sort=message_type. The cap is enforced here, like the body size limit + // above, because it is an EFCore storage limit the persister owns rather than a shared + // ingestion rule. EnclosedMessageTypes' first comma token is a type's full name, so the cap can + // only ever bite on pathological generic names, where a truncated sort key still sorts and + // groups consistently. + static string? TruncateForColumn(string? value) => + value is { Length: > ColumnLengths.ShortTextLength } ? value[..ColumnLengths.ShortTextLength] : value; + static T? GetMetadata(FailedMessage.ProcessingAttempt processingAttempt, string key) => processingAttempt.MessageMetadata.TryGetValue(key, out var value) && value is T typed ? typed : default; } diff --git a/src/ServiceControl.Persistence.Tests/EFCore/ErrorIngestionTests.cs b/src/ServiceControl.Persistence.Tests/EFCore/ErrorIngestionTests.cs index 84510d54e5..3dda29fcf2 100644 --- a/src/ServiceControl.Persistence.Tests/EFCore/ErrorIngestionTests.cs +++ b/src/ServiceControl.Persistence.Tests/EFCore/ErrorIngestionTests.cs @@ -60,6 +60,26 @@ public async Task First_failure_stores_the_message() } } + // The MessageType column is length-bounded (ShortTextLength) so it can be an index key serving + // sort=message_type, and ingestion caps what it writes to it. The full value stays in the + // headers: a retry deserializes from the EnclosedMessageTypes header, never from the column. + [Test] + public async Task An_over_length_message_type_is_capped_in_the_column_but_kept_in_the_headers() + { + var overLengthType = new string('A', 450) + new string('B', 50); + var failure = new IngestedFailure { MessageType = overLengthType }; + + await Ingest(failure); + + var row = await GetFailedMessage(failure.UniqueMessageId); + + using (Assert.EnterMultipleScope()) + { + Assert.That(row.MessageType, Is.EqualTo(overLengthType[..450])); + Assert.That(row.HeadersJson, Does.Contain(overLengthType), "the headers keep the complete type"); + } + } + [Test] public async Task Later_attempt_replaces_the_stored_attempt() { From c3f4efba89238d0c8a005b53b7eebe361cc3cda0 Mon Sep 17 00:00:00 2001 From: Rhys Bevilaqua Date: Fri, 11 Sep 2026 12:15:09 +0800 Subject: [PATCH 4/4] Collapse migrations down to a single step --- ...603_WidenGroupAggregateIndexes.Designer.cs | 918 ----------------- ...ddStatusLastTimeOfFailureIndex.Designer.cs | 921 ------------------ ...1010148_AddStatusLastTimeOfFailureIndex.cs | 27 - ...0260911031949_AddStatusMessageTypeIndex.cs | 50 - ...60911041328_AddMissingIndexes.Designer.cs} | 4 +- ...cs => 20260911041328_AddMissingIndexes.cs} | 43 +- ...547_WidenGroupAggregateIndexes.Designer.cs | 738 -------------- ...ddStatusLastTimeOfFailureIndex.Designer.cs | 740 -------------- ...1010140_AddStatusLastTimeOfFailureIndex.cs | 27 - ...0260911031929_AddStatusMessageTypeIndex.cs | 50 - ...60911041253_AddMissingIndexes.Designer.cs} | 4 +- ...cs => 20260911041253_AddMissingIndexes.cs} | 43 +- 12 files changed, 88 insertions(+), 3477 deletions(-) delete mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs rename src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/{20260911031949_AddStatusMessageTypeIndex.Designer.cs => 20260911041328_AddMissingIndexes.Designer.cs} (99%) rename src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/{20260910054603_WidenGroupAggregateIndexes.cs => 20260911041328_AddMissingIndexes.cs} (53%) delete mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs delete mode 100644 src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs rename src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/{20260911031929_AddStatusMessageTypeIndex.Designer.cs => 20260911041253_AddMissingIndexes.Designer.cs} (99%) rename src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/{20260910054547_WidenGroupAggregateIndexes.cs => 20260911041253_AddMissingIndexes.cs} (52%) diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs deleted file mode 100644 index 53f9df4a01..0000000000 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.Designer.cs +++ /dev/null @@ -1,918 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using ServiceControl.Persistence.EFCore.PostgreSql; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations -{ - [DbContext(typeof(PostgreSqlServiceControlDbContext))] - [Migration("20260910054603_WidenGroupAggregateIndexes")] - partial class WidenGroupAggregateIndexes - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("request_id"); - - b.Property("ArchiveType") - .HasColumnType("integer") - .HasColumnName("archive_type"); - - b.Property("OperationType") - .HasColumnType("integer") - .HasColumnName("operation_type"); - - b.Property("CurrentBatch") - .HasColumnType("integer") - .HasColumnName("current_batch"); - - b.Property("GroupName") - .IsRequired() - .HasColumnType("text") - .HasColumnName("group_name"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_id"); - - b.Property("InitiatedByName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_name"); - - b.Property("NumberOfBatches") - .HasColumnType("integer") - .HasColumnName("number_of_batches"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("operation_id"); - - b.Property("Started") - .HasColumnType("timestamp with time zone") - .HasColumnName("started"); - - b.Property("TotalNumberOfMessages") - .HasColumnType("integer") - .HasColumnName("total_number_of_messages"); - - b.HasKey("RequestId", "ArchiveType", "OperationType") - .HasName("pk_archive_operations"); - - b.HasIndex("Started") - .HasDatabaseName("ix_archive_operations_started"); - - b.ToTable("archive_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Category") - .IsRequired() - .HasColumnType("text") - .HasColumnName("category"); - - b.Property("CustomCheckId") - .IsRequired() - .HasColumnType("text") - .HasColumnName("custom_check_id"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("OriginatingEndpointHost") - .IsRequired() - .HasColumnType("text") - .HasColumnName("originating_endpoint_host"); - - b.Property("OriginatingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("originating_endpoint_host_id"); - - b.Property("OriginatingEndpointName") - .IsRequired() - .HasColumnType("text") - .HasColumnName("originating_endpoint_name"); - - b.Property("ReportedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reported_at"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.HasKey("Id") - .HasName("pk_custom_checks"); - - b.HasIndex("ReportedAt") - .HasDatabaseName("ix_custom_checks_reported_at"); - - b.HasIndex("Status", "ReportedAt") - .HasDatabaseName("ix_custom_checks_status_reported_at"); - - b.ToTable("custom_checks", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => - { - b.Property("Name") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("name"); - - b.Property("TrackInstances") - .HasColumnType("boolean") - .HasColumnName("track_instances"); - - b.HasKey("Name") - .HasName("pk_endpoint_settings"); - - b.ToTable("endpoint_settings", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Category") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("category"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("event_type"); - - b.Property("RaisedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("raised_at"); - - b.PrimitiveCollection>("RelatedTo") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("related_to"); - - b.Property("Severity") - .HasColumnType("integer") - .HasColumnName("severity"); - - b.HasKey("Id") - .HasName("pk_event_log_items"); - - b.HasIndex("RaisedAt", "Id") - .IsDescending() - .HasDatabaseName("ix_event_log_items_raised_at_id"); - - b.ToTable("event_log_items", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DispatchContextJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("dispatch_context_json"); - - b.Property("DispatchContextTypeName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("dispatch_context_type_name"); - - b.HasKey("Id") - .HasName("pk_external_integration_dispatch_requests"); - - b.ToTable("external_integration_dispatch_requests", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("Body") - .IsRequired() - .HasColumnType("bytea") - .HasColumnName("body"); - - b.Property("BodyStoredExternally") - .HasColumnType("boolean") - .HasColumnName("body_stored_externally"); - - b.Property("ExceptionInfo") - .IsRequired() - .HasColumnType("text") - .HasColumnName("exception_info"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("headers_json"); - - b.Property("MessageId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("message_id"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_error_imports"); - - b.HasIndex("FailedAt") - .HasDatabaseName("ix_failed_error_imports_failed_at"); - - b.ToTable("failed_error_imports", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("EditId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("edit_id"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_message_edits"); - - b.HasIndex("EditId") - .HasDatabaseName("ix_failed_message_edits_edit_id"); - - b.ToTable("failed_message_edits", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("BodyContentType") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("body_content_type"); - - b.Property("BodySize") - .HasColumnType("integer") - .HasColumnName("body_size"); - - b.Property("BodyStoredExternally") - .HasColumnType("boolean") - .HasColumnName("body_stored_externally"); - - b.Property("BodyText") - .HasColumnType("text") - .HasColumnName("body_text"); - - b.Property("ConversationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("conversation_id"); - - b.Property("ExceptionMessage") - .HasColumnType("text") - .HasColumnName("exception_message"); - - b.Property("ExceptionType") - .HasColumnType("text") - .HasColumnName("exception_type"); - - b.Property("FailingEndpointAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("failing_endpoint_address"); - - b.Property("FirstTimeOfFailure") - .HasColumnType("timestamp with time zone") - .HasColumnName("first_time_of_failure"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("headers_json"); - - b.Property("IsSystemMessage") - .HasColumnType("boolean") - .HasColumnName("is_system_message"); - - b.Property("LastAttemptedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempted_at"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_modified"); - - b.Property("LastTimeOfFailure") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_time_of_failure"); - - b.Property("MessageId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("message_id"); - - b.Property("MessageType") - .HasColumnType("text") - .HasColumnName("message_type"); - - b.Property("NumberOfProcessingAttempts") - .HasColumnType("integer") - .HasColumnName("number_of_processing_attempts"); - - b.Property("ReceivingEndpointHost") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("receiving_endpoint_host"); - - b.Property("ReceivingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("receiving_endpoint_host_id"); - - b.Property("ReceivingEndpointName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("receiving_endpoint_name"); - - b.Property("SendingEndpointHost") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sending_endpoint_host"); - - b.Property("SendingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("sending_endpoint_host_id"); - - b.Property("SendingEndpointName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sending_endpoint_name"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.Property("StatusChangedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("status_changed_at"); - - b.Property("TimeSent") - .HasColumnType("timestamp with time zone") - .HasColumnName("time_sent"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_messages"); - - b.HasIndex("ConversationId") - .HasDatabaseName("ix_failed_messages_conversation_id"); - - b.HasIndex("FailingEndpointAddress") - .HasDatabaseName("ix_failed_messages_failing_endpoint_address"); - - b.HasIndex("ReceivingEndpointName") - .HasDatabaseName("ix_failed_messages_receiving_endpoint_name"); - - b.HasIndex("StatusChangedAt") - .HasDatabaseName("ix_failed_messages_status_changed_at") - .HasFilter("status IN (2, 4)"); - - b.HasIndex("TimeSent") - .HasDatabaseName("ix_failed_messages_time_sent"); - - b.HasIndex("Status", "LastModified") - .HasDatabaseName("ix_failed_messages_status_last_modified"); - - NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); - - b.ToTable("failed_messages", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.Property("FailedMessageUniqueId") - .HasColumnType("uuid") - .HasColumnName("failed_message_unique_id"); - - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("group_id"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("type"); - - b.HasKey("FailedMessageUniqueId", "GroupId") - .HasName("pk_failed_message_groups"); - - b.HasIndex("GroupId") - .HasDatabaseName("ix_failed_message_groups_group_id"); - - b.HasIndex("Type", "GroupId") - .HasDatabaseName("ix_failed_message_groups_type_group_id"); - - NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); - - b.ToTable("failed_message_groups", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("RetryBatchId") - .HasColumnType("uuid") - .HasColumnName("retry_batch_id"); - - b.Property("StageAttempts") - .HasColumnType("integer") - .HasColumnName("stage_attempts"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_message_retries"); - - b.HasIndex("RetryBatchId") - .HasDatabaseName("ix_failed_message_retries_retry_batch_id"); - - b.ToTable("failed_message_retries", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => - { - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("group_id"); - - b.Property("Comment") - .IsRequired() - .HasColumnType("text") - .HasColumnName("comment"); - - b.HasKey("GroupId") - .HasName("pk_group_comments"); - - b.ToTable("group_comments", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CompletionTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("completion_time"); - - b.Property("Failed") - .HasColumnType("boolean") - .HasColumnName("failed"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(400) - .HasColumnType("character varying(400)") - .HasColumnName("request_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.HasKey("Id") - .HasName("pk_historic_retry_operations"); - - b.HasIndex("CompletionTime", "Id") - .IsDescending() - .HasDatabaseName("ix_historic_retry_operations_completion_time_id"); - - b.ToTable("historic_retry_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("host"); - - b.Property("HostId") - .HasColumnType("uuid") - .HasColumnName("host_id"); - - b.Property("Monitored") - .HasColumnType("boolean") - .HasColumnName("monitored"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("name"); - - b.HasKey("Id") - .HasName("pk_known_endpoints"); - - b.ToTable("known_endpoints", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("normalized_name"); - - b.Property("ThroughputSource") - .HasColumnType("integer") - .HasColumnName("throughput_source"); - - b.PrimitiveCollection>("EndpointIndicators") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("endpoint_indicators"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NormalizedSanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("normalized_sanitized_name"); - - b.Property("SanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sanitized_name"); - - b.Property("Scope") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("scope"); - - b.Property("UserIndicator") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("user_indicator"); - - b.HasKey("NormalizedName", "ThroughputSource") - .HasName("pk_licensing_endpoints"); - - b.HasIndex("NormalizedSanitizedName") - .HasDatabaseName("ix_licensing_endpoints_normalized_sanitized_name"); - - b.ToTable("licensing_endpoints", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("normalized_name"); - - b.Property("ThroughputSource") - .HasColumnType("integer") - .HasColumnName("throughput_source"); - - b.Property("DateUtc") - .HasColumnType("date") - .HasColumnName("date_utc"); - - b.Property("MessageCount") - .HasColumnType("bigint") - .HasColumnName("message_count"); - - b.HasKey("NormalizedName", "ThroughputSource", "DateUtc") - .HasName("pk_licensing_endpoint_throughput"); - - b.HasIndex("DateUtc") - .HasDatabaseName("ix_licensing_endpoint_throughput_date_utc"); - - b.ToTable("licensing_endpoint_throughput", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => - { - b.Property("FromPhysicalAddress") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("from_physical_address"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_modified"); - - b.Property("ToPhysicalAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("to_physical_address"); - - b.HasKey("FromPhysicalAddress") - .HasName("pk_message_redirects"); - - b.ToTable("message_redirects", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Classifier") - .HasColumnType("text") - .HasColumnName("classifier"); - - b.Property("Context") - .HasColumnType("text") - .HasColumnName("context"); - - b.Property("InitialBatchSize") - .HasColumnType("integer") - .HasColumnName("initial_batch_size"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_id"); - - b.Property("InitiatedByName") - .HasColumnType("text") - .HasColumnName("initiated_by_name"); - - b.Property("Last") - .HasColumnType("timestamp with time zone") - .HasColumnName("last"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("operation_id"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("request_id"); - - b.Property("RetrySessionId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("retry_session_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("StagingId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("staging_id"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.HasKey("Id") - .HasName("pk_retry_batches"); - - b.HasIndex("Status", "RetrySessionId") - .HasDatabaseName("ix_retry_batches_status_retry_session_id"); - - b.ToTable("retry_batches", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => - { - b.Property("Id") - .HasColumnType("integer") - .HasColumnName("id"); - - b.Property("RetryBatchId") - .HasColumnType("uuid") - .HasColumnName("retry_batch_id"); - - b.HasKey("Id") - .HasName("pk_retry_batch_now_forwarding"); - - b.ToTable("retry_batch_now_forwarding", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => - { - b.Property("Key") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("key"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text") - .HasColumnName("value"); - - b.HasKey("Key") - .HasName("pk_settings"); - - b.ToTable("settings", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => - { - b.Property("MessageType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("message_type"); - - b.Property("TransportAddress") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("transport_address"); - - b.Property("Endpoint") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("endpoint"); - - b.HasKey("MessageType", "TransportAddress") - .HasName("pk_subscriptions"); - - b.ToTable("subscriptions", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(400) - .HasColumnType("character varying(400)") - .HasColumnName("request_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("Classifier") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("classifier"); - - b.Property("CompletionTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("completion_time"); - - b.Property("Failed") - .HasColumnType("boolean") - .HasColumnName("failed"); - - b.Property("Last") - .HasColumnType("timestamp with time zone") - .HasColumnName("last"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.HasKey("RequestId", "RetryType") - .HasName("pk_unacknowledged_retry_operations"); - - b.ToTable("unacknowledged_retry_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) - .WithMany() - .HasForeignKey("FailedMessageUniqueId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_failed_message_groups_failed_messages_failed_message_unique"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) - .WithMany() - .HasForeignKey("NormalizedName", "ThroughputSource") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_licensing_endpoint_throughput_licensing_endpoints_normalize"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs deleted file mode 100644 index 67f07b61cd..0000000000 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.Designer.cs +++ /dev/null @@ -1,921 +0,0 @@ -// -using System; -using System.Collections.Generic; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using ServiceControl.Persistence.EFCore.PostgreSql; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations -{ - [DbContext(typeof(PostgreSqlServiceControlDbContext))] - [Migration("20260911010148_AddStatusLastTimeOfFailureIndex")] - partial class AddStatusLastTimeOfFailureIndex - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("request_id"); - - b.Property("ArchiveType") - .HasColumnType("integer") - .HasColumnName("archive_type"); - - b.Property("OperationType") - .HasColumnType("integer") - .HasColumnName("operation_type"); - - b.Property("CurrentBatch") - .HasColumnType("integer") - .HasColumnName("current_batch"); - - b.Property("GroupName") - .IsRequired() - .HasColumnType("text") - .HasColumnName("group_name"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_id"); - - b.Property("InitiatedByName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_name"); - - b.Property("NumberOfBatches") - .HasColumnType("integer") - .HasColumnName("number_of_batches"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("operation_id"); - - b.Property("Started") - .HasColumnType("timestamp with time zone") - .HasColumnName("started"); - - b.Property("TotalNumberOfMessages") - .HasColumnType("integer") - .HasColumnName("total_number_of_messages"); - - b.HasKey("RequestId", "ArchiveType", "OperationType") - .HasName("pk_archive_operations"); - - b.HasIndex("Started") - .HasDatabaseName("ix_archive_operations_started"); - - b.ToTable("archive_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Category") - .IsRequired() - .HasColumnType("text") - .HasColumnName("category"); - - b.Property("CustomCheckId") - .IsRequired() - .HasColumnType("text") - .HasColumnName("custom_check_id"); - - b.Property("FailureReason") - .HasColumnType("text") - .HasColumnName("failure_reason"); - - b.Property("OriginatingEndpointHost") - .IsRequired() - .HasColumnType("text") - .HasColumnName("originating_endpoint_host"); - - b.Property("OriginatingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("originating_endpoint_host_id"); - - b.Property("OriginatingEndpointName") - .IsRequired() - .HasColumnType("text") - .HasColumnName("originating_endpoint_name"); - - b.Property("ReportedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("reported_at"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.HasKey("Id") - .HasName("pk_custom_checks"); - - b.HasIndex("ReportedAt") - .HasDatabaseName("ix_custom_checks_reported_at"); - - b.HasIndex("Status", "ReportedAt") - .HasDatabaseName("ix_custom_checks_status_reported_at"); - - b.ToTable("custom_checks", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => - { - b.Property("Name") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("name"); - - b.Property("TrackInstances") - .HasColumnType("boolean") - .HasColumnName("track_instances"); - - b.HasKey("Name") - .HasName("pk_endpoint_settings"); - - b.ToTable("endpoint_settings", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Category") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("category"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text") - .HasColumnName("description"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("event_type"); - - b.Property("RaisedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("raised_at"); - - b.PrimitiveCollection>("RelatedTo") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("related_to"); - - b.Property("Severity") - .HasColumnType("integer") - .HasColumnName("severity"); - - b.HasKey("Id") - .HasName("pk_event_log_items"); - - b.HasIndex("RaisedAt", "Id") - .IsDescending() - .HasDatabaseName("ix_event_log_items_raised_at_id"); - - b.ToTable("event_log_items", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DispatchContextJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("dispatch_context_json"); - - b.Property("DispatchContextTypeName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("dispatch_context_type_name"); - - b.HasKey("Id") - .HasName("pk_external_integration_dispatch_requests"); - - b.ToTable("external_integration_dispatch_requests", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("Body") - .IsRequired() - .HasColumnType("bytea") - .HasColumnName("body"); - - b.Property("BodyStoredExternally") - .HasColumnType("boolean") - .HasColumnName("body_stored_externally"); - - b.Property("ExceptionInfo") - .IsRequired() - .HasColumnType("text") - .HasColumnName("exception_info"); - - b.Property("FailedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("failed_at"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("headers_json"); - - b.Property("MessageId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("message_id"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_error_imports"); - - b.HasIndex("FailedAt") - .HasDatabaseName("ix_failed_error_imports_failed_at"); - - b.ToTable("failed_error_imports", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("EditId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("edit_id"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_message_edits"); - - b.HasIndex("EditId") - .HasDatabaseName("ix_failed_message_edits_edit_id"); - - b.ToTable("failed_message_edits", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("BodyContentType") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("body_content_type"); - - b.Property("BodySize") - .HasColumnType("integer") - .HasColumnName("body_size"); - - b.Property("BodyStoredExternally") - .HasColumnType("boolean") - .HasColumnName("body_stored_externally"); - - b.Property("BodyText") - .HasColumnType("text") - .HasColumnName("body_text"); - - b.Property("ConversationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("conversation_id"); - - b.Property("ExceptionMessage") - .HasColumnType("text") - .HasColumnName("exception_message"); - - b.Property("ExceptionType") - .HasColumnType("text") - .HasColumnName("exception_type"); - - b.Property("FailingEndpointAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("failing_endpoint_address"); - - b.Property("FirstTimeOfFailure") - .HasColumnType("timestamp with time zone") - .HasColumnName("first_time_of_failure"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("text") - .HasColumnName("headers_json"); - - b.Property("IsSystemMessage") - .HasColumnType("boolean") - .HasColumnName("is_system_message"); - - b.Property("LastAttemptedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_attempted_at"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_modified"); - - b.Property("LastTimeOfFailure") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_time_of_failure"); - - b.Property("MessageId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("message_id"); - - b.Property("MessageType") - .HasColumnType("text") - .HasColumnName("message_type"); - - b.Property("NumberOfProcessingAttempts") - .HasColumnType("integer") - .HasColumnName("number_of_processing_attempts"); - - b.Property("ReceivingEndpointHost") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("receiving_endpoint_host"); - - b.Property("ReceivingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("receiving_endpoint_host_id"); - - b.Property("ReceivingEndpointName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("receiving_endpoint_name"); - - b.Property("SendingEndpointHost") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sending_endpoint_host"); - - b.Property("SendingEndpointHostId") - .HasColumnType("uuid") - .HasColumnName("sending_endpoint_host_id"); - - b.Property("SendingEndpointName") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sending_endpoint_name"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.Property("StatusChangedAt") - .HasColumnType("timestamp with time zone") - .HasColumnName("status_changed_at"); - - b.Property("TimeSent") - .HasColumnType("timestamp with time zone") - .HasColumnName("time_sent"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_messages"); - - b.HasIndex("ConversationId") - .HasDatabaseName("ix_failed_messages_conversation_id"); - - b.HasIndex("FailingEndpointAddress") - .HasDatabaseName("ix_failed_messages_failing_endpoint_address"); - - b.HasIndex("ReceivingEndpointName") - .HasDatabaseName("ix_failed_messages_receiving_endpoint_name"); - - b.HasIndex("StatusChangedAt") - .HasDatabaseName("ix_failed_messages_status_changed_at") - .HasFilter("status IN (2, 4)"); - - b.HasIndex("TimeSent") - .HasDatabaseName("ix_failed_messages_time_sent"); - - b.HasIndex("Status", "LastModified") - .HasDatabaseName("ix_failed_messages_status_last_modified"); - - NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); - - b.HasIndex("Status", "LastTimeOfFailure") - .HasDatabaseName("ix_failed_messages_status_last_time_of_failure"); - - b.ToTable("failed_messages", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.Property("FailedMessageUniqueId") - .HasColumnType("uuid") - .HasColumnName("failed_message_unique_id"); - - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("group_id"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text") - .HasColumnName("title"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("type"); - - b.HasKey("FailedMessageUniqueId", "GroupId") - .HasName("pk_failed_message_groups"); - - b.HasIndex("GroupId") - .HasDatabaseName("ix_failed_message_groups_group_id"); - - b.HasIndex("Type", "GroupId") - .HasDatabaseName("ix_failed_message_groups_type_group_id"); - - NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); - - b.ToTable("failed_message_groups", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uuid") - .HasColumnName("unique_message_id"); - - b.Property("RetryBatchId") - .HasColumnType("uuid") - .HasColumnName("retry_batch_id"); - - b.Property("StageAttempts") - .HasColumnType("integer") - .HasColumnName("stage_attempts"); - - b.HasKey("UniqueMessageId") - .HasName("pk_failed_message_retries"); - - b.HasIndex("RetryBatchId") - .HasDatabaseName("ix_failed_message_retries_retry_batch_id"); - - b.ToTable("failed_message_retries", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => - { - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("character varying(64)") - .HasColumnName("group_id"); - - b.Property("Comment") - .IsRequired() - .HasColumnType("text") - .HasColumnName("comment"); - - b.HasKey("GroupId") - .HasName("pk_group_comments"); - - b.ToTable("group_comments", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasColumnName("id"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CompletionTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("completion_time"); - - b.Property("Failed") - .HasColumnType("boolean") - .HasColumnName("failed"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(400) - .HasColumnType("character varying(400)") - .HasColumnName("request_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.HasKey("Id") - .HasName("pk_historic_retry_operations"); - - b.HasIndex("CompletionTime", "Id") - .IsDescending() - .HasDatabaseName("ix_historic_retry_operations_completion_time_id"); - - b.ToTable("historic_retry_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("host"); - - b.Property("HostId") - .HasColumnType("uuid") - .HasColumnName("host_id"); - - b.Property("Monitored") - .HasColumnType("boolean") - .HasColumnName("monitored"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("name"); - - b.HasKey("Id") - .HasName("pk_known_endpoints"); - - b.ToTable("known_endpoints", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("normalized_name"); - - b.Property("ThroughputSource") - .HasColumnType("integer") - .HasColumnName("throughput_source"); - - b.PrimitiveCollection>("EndpointIndicators") - .IsRequired() - .HasColumnType("text[]") - .HasColumnName("endpoint_indicators"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("name"); - - b.Property("NormalizedSanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("normalized_sanitized_name"); - - b.Property("SanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("sanitized_name"); - - b.Property("Scope") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("scope"); - - b.Property("UserIndicator") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("user_indicator"); - - b.HasKey("NormalizedName", "ThroughputSource") - .HasName("pk_licensing_endpoints"); - - b.HasIndex("NormalizedSanitizedName") - .HasDatabaseName("ix_licensing_endpoints_normalized_sanitized_name"); - - b.ToTable("licensing_endpoints", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("character varying(300)") - .HasColumnName("normalized_name"); - - b.Property("ThroughputSource") - .HasColumnType("integer") - .HasColumnName("throughput_source"); - - b.Property("DateUtc") - .HasColumnType("date") - .HasColumnName("date_utc"); - - b.Property("MessageCount") - .HasColumnType("bigint") - .HasColumnName("message_count"); - - b.HasKey("NormalizedName", "ThroughputSource", "DateUtc") - .HasName("pk_licensing_endpoint_throughput"); - - b.HasIndex("DateUtc") - .HasDatabaseName("ix_licensing_endpoint_throughput_date_utc"); - - b.ToTable("licensing_endpoint_throughput", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => - { - b.Property("FromPhysicalAddress") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("from_physical_address"); - - b.Property("LastModified") - .HasColumnType("timestamp with time zone") - .HasColumnName("last_modified"); - - b.Property("ToPhysicalAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("to_physical_address"); - - b.HasKey("FromPhysicalAddress") - .HasName("pk_message_redirects"); - - b.ToTable("message_redirects", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => - { - b.Property("Id") - .HasColumnType("uuid") - .HasColumnName("id"); - - b.Property("Classifier") - .HasColumnType("text") - .HasColumnName("classifier"); - - b.Property("Context") - .HasColumnType("text") - .HasColumnName("context"); - - b.Property("InitialBatchSize") - .HasColumnType("integer") - .HasColumnName("initial_batch_size"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("initiated_by_id"); - - b.Property("InitiatedByName") - .HasColumnType("text") - .HasColumnName("initiated_by_name"); - - b.Property("Last") - .HasColumnType("timestamp with time zone") - .HasColumnName("last"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("operation_id"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("request_id"); - - b.Property("RetrySessionId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("retry_session_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("StagingId") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("staging_id"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.Property("Status") - .HasColumnType("integer") - .HasColumnName("status"); - - b.HasKey("Id") - .HasName("pk_retry_batches"); - - b.HasIndex("Status", "RetrySessionId") - .HasDatabaseName("ix_retry_batches_status_retry_session_id"); - - b.ToTable("retry_batches", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => - { - b.Property("Id") - .HasColumnType("integer") - .HasColumnName("id"); - - b.Property("RetryBatchId") - .HasColumnType("uuid") - .HasColumnName("retry_batch_id"); - - b.HasKey("Id") - .HasName("pk_retry_batch_now_forwarding"); - - b.ToTable("retry_batch_now_forwarding", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => - { - b.Property("Key") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("key"); - - b.Property("Value") - .IsRequired() - .HasColumnType("text") - .HasColumnName("value"); - - b.HasKey("Key") - .HasName("pk_settings"); - - b.ToTable("settings", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => - { - b.Property("MessageType") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("message_type"); - - b.Property("TransportAddress") - .HasMaxLength(200) - .HasColumnType("character varying(200)") - .HasColumnName("transport_address"); - - b.Property("Endpoint") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("endpoint"); - - b.HasKey("MessageType", "TransportAddress") - .HasName("pk_subscriptions"); - - b.ToTable("subscriptions", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(400) - .HasColumnType("character varying(400)") - .HasColumnName("request_id"); - - b.Property("RetryType") - .HasColumnType("integer") - .HasColumnName("retry_type"); - - b.Property("Classifier") - .HasMaxLength(450) - .HasColumnType("character varying(450)") - .HasColumnName("classifier"); - - b.Property("CompletionTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("completion_time"); - - b.Property("Failed") - .HasColumnType("boolean") - .HasColumnName("failed"); - - b.Property("Last") - .HasColumnType("timestamp with time zone") - .HasColumnName("last"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("integer") - .HasColumnName("number_of_messages_processed"); - - b.Property("Originator") - .HasColumnType("text") - .HasColumnName("originator"); - - b.Property("StartTime") - .HasColumnType("timestamp with time zone") - .HasColumnName("start_time"); - - b.HasKey("RequestId", "RetryType") - .HasName("pk_unacknowledged_retry_operations"); - - b.ToTable("unacknowledged_retry_operations", (string)null); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) - .WithMany() - .HasForeignKey("FailedMessageUniqueId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_failed_message_groups_failed_messages_failed_message_unique"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) - .WithMany() - .HasForeignKey("NormalizedName", "ThroughputSource") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_licensing_endpoint_throughput_licensing_endpoints_normalize"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs deleted file mode 100644 index df6e0c1bd5..0000000000 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911010148_AddStatusLastTimeOfFailureIndex.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations -{ - /// - public partial class AddStatusLastTimeOfFailureIndex : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "ix_failed_messages_status_last_time_of_failure", - table: "failed_messages", - columns: new[] { "status", "last_time_of_failure" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "ix_failed_messages_status_last_time_of_failure", - table: "failed_messages"); - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs deleted file mode 100644 index 5833b84aa4..0000000000 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations -{ - /// - public partial class AddStatusMessageTypeIndex : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "message_type", - table: "failed_messages", - type: "character varying(450)", - maxLength: 450, - nullable: true, - oldClrType: typeof(string), - oldType: "text", - oldNullable: true); - - //normally this would be potentially slow and necessitate executing outside of the - //migration transaction, but no customers consume this persister yet so this can - //treated as an initial migration - migrationBuilder.CreateIndex( - name: "ix_failed_messages_status_message_type_unique_message_id", - table: "failed_messages", - columns: new[] { "status", "message_type", "unique_message_id" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "ix_failed_messages_status_message_type_unique_message_id", - table: "failed_messages"); - - migrationBuilder.AlterColumn( - name: "message_type", - table: "failed_messages", - type: "text", - nullable: true, - oldClrType: typeof(string), - oldType: "character varying(450)", - oldMaxLength: 450, - oldNullable: true); - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.Designer.cs similarity index 99% rename from src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.Designer.cs index c08897f124..94ef5611d2 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911031949_AddStatusMessageTypeIndex.Designer.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.Designer.cs @@ -13,8 +13,8 @@ namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations { [DbContext(typeof(PostgreSqlServiceControlDbContext))] - [Migration("20260911031949_AddStatusMessageTypeIndex")] - partial class AddStatusMessageTypeIndex + [Migration("20260911041328_AddMissingIndexes")] + partial class AddMissingIndexes { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.cs similarity index 53% rename from src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs rename to src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.cs index 9d82efed94..bd04378261 100644 --- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260910054603_WidenGroupAggregateIndexes.cs +++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260911041328_AddMissingIndexes.cs @@ -5,7 +5,7 @@ namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations { /// - public partial class WidenGroupAggregateIndexes : Migration + public partial class AddMissingIndexes : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -18,12 +18,35 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "ix_failed_message_groups_type_group_id", table: "failed_message_groups"); + migrationBuilder.AlterColumn( + name: "message_type", + table: "failed_messages", + type: "character varying(450)", + maxLength: 450, + nullable: true, + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + migrationBuilder.CreateIndex( name: "ix_failed_messages_status_last_modified", table: "failed_messages", columns: new[] { "status", "last_modified" }) .Annotation("Npgsql:IndexInclude", new[] { "first_time_of_failure", "last_time_of_failure" }); + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_last_time_of_failure", + table: "failed_messages", + columns: new[] { "status", "last_time_of_failure" }); + + // Normally this would be potentially slow and necessitate executing outside of the + // migration transaction, but no customers consume this persister yet so this can + // be treated as an initial migration + migrationBuilder.CreateIndex( + name: "ix_failed_messages_status_message_type_unique_message_id", + table: "failed_messages", + columns: new[] { "status", "message_type", "unique_message_id" }); + migrationBuilder.CreateIndex( name: "ix_failed_message_groups_type_group_id", table: "failed_message_groups", @@ -38,10 +61,28 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "ix_failed_messages_status_last_modified", table: "failed_messages"); + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_last_time_of_failure", + table: "failed_messages"); + + migrationBuilder.DropIndex( + name: "ix_failed_messages_status_message_type_unique_message_id", + table: "failed_messages"); + migrationBuilder.DropIndex( name: "ix_failed_message_groups_type_group_id", table: "failed_message_groups"); + migrationBuilder.AlterColumn( + name: "message_type", + table: "failed_messages", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "character varying(450)", + oldMaxLength: 450, + oldNullable: true); + migrationBuilder.CreateIndex( name: "ix_failed_messages_status_last_modified", table: "failed_messages", diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs deleted file mode 100644 index 24d793444c..0000000000 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.Designer.cs +++ /dev/null @@ -1,738 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using ServiceControl.Persistence.EFCore.SqlServer; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerServiceControlDbContext))] - [Migration("20260910054547_WidenGroupAggregateIndexes")] - partial class WidenGroupAggregateIndexes - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.10") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("ArchiveType") - .HasColumnType("int"); - - b.Property("OperationType") - .HasColumnType("int"); - - b.Property("CurrentBatch") - .HasColumnType("int"); - - b.Property("GroupName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("InitiatedByName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("NumberOfBatches") - .HasColumnType("int"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Started") - .HasColumnType("datetime2"); - - b.Property("TotalNumberOfMessages") - .HasColumnType("int"); - - b.HasKey("RequestId", "ArchiveType", "OperationType"); - - b.HasIndex("Started"); - - b.ToTable("ArchiveOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Category") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CustomCheckId") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FailureReason") - .HasColumnType("nvarchar(max)"); - - b.Property("OriginatingEndpointHost") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("OriginatingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("OriginatingEndpointName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ReportedAt") - .HasColumnType("datetime2"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("ReportedAt"); - - b.HasIndex("Status", "ReportedAt"); - - b.ToTable("CustomChecks"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => - { - b.Property("Name") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("TrackInstances") - .HasColumnType("bit"); - - b.HasKey("Name"); - - b.ToTable("EndpointSettings"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Category") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RaisedAt") - .HasColumnType("datetime2"); - - b.PrimitiveCollection("RelatedTo") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Severity") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("RaisedAt", "Id") - .IsDescending(); - - b.ToTable("EventLogItems"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("DispatchContextJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DispatchContextTypeName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.ToTable("ExternalIntegrationDispatchRequests"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("Body") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.Property("BodyStoredExternally") - .HasColumnType("bit"); - - b.Property("ExceptionInfo") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FailedAt") - .HasColumnType("datetime2"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("MessageId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("FailedAt"); - - b.ToTable("FailedErrorImports"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("EditId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("EditId"); - - b.ToTable("FailedMessageEdits"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("BodyContentType") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("BodySize") - .HasColumnType("int"); - - b.Property("BodyStoredExternally") - .HasColumnType("bit"); - - b.Property("BodyText") - .HasColumnType("nvarchar(max)"); - - b.Property("ConversationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("ExceptionMessage") - .HasColumnType("nvarchar(max)"); - - b.Property("ExceptionType") - .HasColumnType("nvarchar(max)"); - - b.Property("FailingEndpointAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("FirstTimeOfFailure") - .HasColumnType("datetime2"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IsSystemMessage") - .HasColumnType("bit"); - - b.Property("LastAttemptedAt") - .HasColumnType("datetime2"); - - b.Property("LastModified") - .HasColumnType("datetime2"); - - b.Property("LastTimeOfFailure") - .HasColumnType("datetime2"); - - b.Property("MessageId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("MessageType") - .HasColumnType("nvarchar(max)"); - - b.Property("NumberOfProcessingAttempts") - .HasColumnType("int"); - - b.Property("ReceivingEndpointHost") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("ReceivingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("ReceivingEndpointName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SendingEndpointHost") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SendingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("SendingEndpointName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("StatusChangedAt") - .HasColumnType("datetime2"); - - b.Property("TimeSent") - .HasColumnType("datetime2"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("ConversationId"); - - b.HasIndex("FailingEndpointAddress"); - - b.HasIndex("ReceivingEndpointName"); - - b.HasIndex("StatusChangedAt") - .HasFilter("[Status] IN (2, 4)"); - - b.HasIndex("TimeSent"); - - b.HasIndex("Status", "LastModified"); - - SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); - - b.ToTable("FailedMessages"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.Property("FailedMessageUniqueId") - .HasColumnType("uniqueidentifier"); - - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("FailedMessageUniqueId", "GroupId"); - - b.HasIndex("GroupId"); - - b.HasIndex("Type", "GroupId"); - - SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); - - b.ToTable("FailedMessageGroups"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("RetryBatchId") - .HasColumnType("uniqueidentifier"); - - b.Property("StageAttempts") - .HasColumnType("int"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("RetryBatchId"); - - b.ToTable("FailedMessageRetries"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => - { - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Comment") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("GroupId"); - - b.ToTable("GroupComments"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompletionTime") - .HasColumnType("datetime2"); - - b.Property("Failed") - .HasColumnType("bit"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(400) - .HasColumnType("nvarchar(400)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("CompletionTime", "Id") - .IsDescending(); - - b.ToTable("HistoricRetryOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("HostId") - .HasColumnType("uniqueidentifier"); - - b.Property("Monitored") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.ToTable("KnownEndpoints"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("ThroughputSource") - .HasColumnType("int"); - - b.PrimitiveCollection("EndpointIndicators") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("NormalizedSanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Scope") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("UserIndicator") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("NormalizedName", "ThroughputSource"); - - b.HasIndex("NormalizedSanitizedName"); - - b.ToTable("LicensingEndpoints"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("ThroughputSource") - .HasColumnType("int"); - - b.Property("DateUtc") - .HasColumnType("date"); - - b.Property("MessageCount") - .HasColumnType("bigint"); - - b.HasKey("NormalizedName", "ThroughputSource", "DateUtc"); - - b.HasIndex("DateUtc"); - - b.ToTable("LicensingEndpointThroughput"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => - { - b.Property("FromPhysicalAddress") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("LastModified") - .HasColumnType("datetime2"); - - b.Property("ToPhysicalAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("FromPhysicalAddress"); - - b.ToTable("MessageRedirects"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Classifier") - .HasColumnType("nvarchar(max)"); - - b.Property("Context") - .HasColumnType("nvarchar(max)"); - - b.Property("InitialBatchSize") - .HasColumnType("int"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("InitiatedByName") - .HasColumnType("nvarchar(max)"); - - b.Property("Last") - .HasColumnType("datetime2"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RetrySessionId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("StagingId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("Status", "RetrySessionId"); - - b.ToTable("RetryBatches"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => - { - b.Property("Id") - .HasColumnType("int"); - - b.Property("RetryBatchId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.ToTable("RetryBatchNowForwarding"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => - { - b.Property("Key") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Key"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => - { - b.Property("MessageType") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("TransportAddress") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("Endpoint") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("MessageType", "TransportAddress"); - - b.ToTable("Subscriptions"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(400) - .HasColumnType("nvarchar(400)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("Classifier") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("CompletionTime") - .HasColumnType("datetime2"); - - b.Property("Failed") - .HasColumnType("bit"); - - b.Property("Last") - .HasColumnType("datetime2"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.HasKey("RequestId", "RetryType"); - - b.ToTable("UnacknowledgedRetryOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) - .WithMany() - .HasForeignKey("FailedMessageUniqueId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) - .WithMany() - .HasForeignKey("NormalizedName", "ThroughputSource") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs deleted file mode 100644 index 208012a8f1..0000000000 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.Designer.cs +++ /dev/null @@ -1,740 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using ServiceControl.Persistence.EFCore.SqlServer; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations -{ - [DbContext(typeof(SqlServerServiceControlDbContext))] - [Migration("20260911010140_AddStatusLastTimeOfFailureIndex")] - partial class AddStatusLastTimeOfFailureIndex - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.11") - .HasAnnotation("Relational:MaxIdentifierLength", 128); - - SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ArchiveOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("ArchiveType") - .HasColumnType("int"); - - b.Property("OperationType") - .HasColumnType("int"); - - b.Property("CurrentBatch") - .HasColumnType("int"); - - b.Property("GroupName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("InitiatedByName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("NumberOfBatches") - .HasColumnType("int"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Started") - .HasColumnType("datetime2"); - - b.Property("TotalNumberOfMessages") - .HasColumnType("int"); - - b.HasKey("RequestId", "ArchiveType", "OperationType"); - - b.HasIndex("Started"); - - b.ToTable("ArchiveOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.CustomCheckEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Category") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("CustomCheckId") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FailureReason") - .HasColumnType("nvarchar(max)"); - - b.Property("OriginatingEndpointHost") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("OriginatingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("OriginatingEndpointName") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("ReportedAt") - .HasColumnType("datetime2"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("ReportedAt"); - - b.HasIndex("Status", "ReportedAt"); - - b.ToTable("CustomChecks"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EndpointSettingsEntity", b => - { - b.Property("Name") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("TrackInstances") - .HasColumnType("bit"); - - b.HasKey("Name"); - - b.ToTable("EndpointSettings"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.EventLogItemEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("Category") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Description") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("EventType") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RaisedAt") - .HasColumnType("datetime2"); - - b.PrimitiveCollection("RelatedTo") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Severity") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("RaisedAt", "Id") - .IsDescending(); - - b.ToTable("EventLogItems"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.ExternalIntegrationDispatchRequestEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("DispatchContextJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("DispatchContextTypeName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.ToTable("ExternalIntegrationDispatchRequests"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedErrorImportEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("Body") - .IsRequired() - .HasColumnType("varbinary(max)"); - - b.Property("BodyStoredExternally") - .HasColumnType("bit"); - - b.Property("ExceptionInfo") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("FailedAt") - .HasColumnType("datetime2"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("MessageId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("FailedAt"); - - b.ToTable("FailedErrorImports"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEditEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("EditId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("EditId"); - - b.ToTable("FailedMessageEdits"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("BodyContentType") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("BodySize") - .HasColumnType("int"); - - b.Property("BodyStoredExternally") - .HasColumnType("bit"); - - b.Property("BodyText") - .HasColumnType("nvarchar(max)"); - - b.Property("ConversationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("ExceptionMessage") - .HasColumnType("nvarchar(max)"); - - b.Property("ExceptionType") - .HasColumnType("nvarchar(max)"); - - b.Property("FailingEndpointAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("FirstTimeOfFailure") - .HasColumnType("datetime2"); - - b.Property("HeadersJson") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("IsSystemMessage") - .HasColumnType("bit"); - - b.Property("LastAttemptedAt") - .HasColumnType("datetime2"); - - b.Property("LastModified") - .HasColumnType("datetime2"); - - b.Property("LastTimeOfFailure") - .HasColumnType("datetime2"); - - b.Property("MessageId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("MessageType") - .HasColumnType("nvarchar(max)"); - - b.Property("NumberOfProcessingAttempts") - .HasColumnType("int"); - - b.Property("ReceivingEndpointHost") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("ReceivingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("ReceivingEndpointName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SendingEndpointHost") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SendingEndpointHostId") - .HasColumnType("uniqueidentifier"); - - b.Property("SendingEndpointName") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Status") - .HasColumnType("int"); - - b.Property("StatusChangedAt") - .HasColumnType("datetime2"); - - b.Property("TimeSent") - .HasColumnType("datetime2"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("ConversationId"); - - b.HasIndex("FailingEndpointAddress"); - - b.HasIndex("ReceivingEndpointName"); - - b.HasIndex("StatusChangedAt") - .HasFilter("[Status] IN (2, 4)"); - - b.HasIndex("TimeSent"); - - b.HasIndex("Status", "LastModified"); - - SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); - - b.HasIndex("Status", "LastTimeOfFailure"); - - b.ToTable("FailedMessages"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.Property("FailedMessageUniqueId") - .HasColumnType("uniqueidentifier"); - - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Title") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Type") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("nvarchar(255)"); - - b.HasKey("FailedMessageUniqueId", "GroupId"); - - b.HasIndex("GroupId"); - - b.HasIndex("Type", "GroupId"); - - SqlServerIndexBuilderExtensions.IncludeProperties(b.HasIndex("Type", "GroupId"), new[] { "Title" }); - - b.ToTable("FailedMessageGroups"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageRetryEntity", b => - { - b.Property("UniqueMessageId") - .HasColumnType("uniqueidentifier"); - - b.Property("RetryBatchId") - .HasColumnType("uniqueidentifier"); - - b.Property("StageAttempts") - .HasColumnType("int"); - - b.HasKey("UniqueMessageId"); - - b.HasIndex("RetryBatchId"); - - b.ToTable("FailedMessageRetries"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.GroupCommentEntity", b => - { - b.Property("GroupId") - .HasMaxLength(64) - .HasColumnType("nvarchar(64)"); - - b.Property("Comment") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("GroupId"); - - b.ToTable("GroupComments"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.HistoricRetryOperationEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); - - b.Property("CompletionTime") - .HasColumnType("datetime2"); - - b.Property("Failed") - .HasColumnType("bit"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(400) - .HasColumnType("nvarchar(400)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.HasKey("Id"); - - b.HasIndex("CompletionTime", "Id") - .IsDescending(); - - b.ToTable("HistoricRetryOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.KnownEndpointEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Host") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("HostId") - .HasColumnType("uniqueidentifier"); - - b.Property("Monitored") - .HasColumnType("bit"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("Id"); - - b.ToTable("KnownEndpoints"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("ThroughputSource") - .HasColumnType("int"); - - b.PrimitiveCollection("EndpointIndicators") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("NormalizedSanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("SanitizedName") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Scope") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("UserIndicator") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("NormalizedName", "ThroughputSource"); - - b.HasIndex("NormalizedSanitizedName"); - - b.ToTable("LicensingEndpoints"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.Property("NormalizedName") - .HasMaxLength(300) - .HasColumnType("nvarchar(300)"); - - b.Property("ThroughputSource") - .HasColumnType("int"); - - b.Property("DateUtc") - .HasColumnType("date"); - - b.Property("MessageCount") - .HasColumnType("bigint"); - - b.HasKey("NormalizedName", "ThroughputSource", "DateUtc"); - - b.HasIndex("DateUtc"); - - b.ToTable("LicensingEndpointThroughput"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.MessageRedirectEntity", b => - { - b.Property("FromPhysicalAddress") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("LastModified") - .HasColumnType("datetime2"); - - b.Property("ToPhysicalAddress") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("FromPhysicalAddress"); - - b.ToTable("MessageRedirects"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchEntity", b => - { - b.Property("Id") - .HasColumnType("uniqueidentifier"); - - b.Property("Classifier") - .HasColumnType("nvarchar(max)"); - - b.Property("Context") - .HasColumnType("nvarchar(max)"); - - b.Property("InitialBatchSize") - .HasColumnType("int"); - - b.Property("InitiatedById") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("InitiatedByName") - .HasColumnType("nvarchar(max)"); - - b.Property("Last") - .HasColumnType("datetime2"); - - b.Property("OperationId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("RequestId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RetrySessionId") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("StagingId") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.Property("Status") - .HasColumnType("int"); - - b.HasKey("Id"); - - b.HasIndex("Status", "RetrySessionId"); - - b.ToTable("RetryBatches"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.RetryBatchNowForwardingEntity", b => - { - b.Property("Id") - .HasColumnType("int"); - - b.Property("RetryBatchId") - .HasColumnType("uniqueidentifier"); - - b.HasKey("Id"); - - b.ToTable("RetryBatchNowForwarding"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SettingEntity", b => - { - b.Property("Key") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("Value") - .IsRequired() - .HasColumnType("nvarchar(max)"); - - b.HasKey("Key"); - - b.ToTable("Settings"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.SubscriptionEntity", b => - { - b.Property("MessageType") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("TransportAddress") - .HasMaxLength(200) - .HasColumnType("nvarchar(200)"); - - b.Property("Endpoint") - .IsRequired() - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.HasKey("MessageType", "TransportAddress"); - - b.ToTable("Subscriptions"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.UnacknowledgedRetryOperationEntity", b => - { - b.Property("RequestId") - .HasMaxLength(400) - .HasColumnType("nvarchar(400)"); - - b.Property("RetryType") - .HasColumnType("int"); - - b.Property("Classifier") - .HasMaxLength(450) - .HasColumnType("nvarchar(450)"); - - b.Property("CompletionTime") - .HasColumnType("datetime2"); - - b.Property("Failed") - .HasColumnType("bit"); - - b.Property("Last") - .HasColumnType("datetime2"); - - b.Property("NumberOfMessagesProcessed") - .HasColumnType("int"); - - b.Property("Originator") - .HasColumnType("nvarchar(max)"); - - b.Property("StartTime") - .HasColumnType("datetime2"); - - b.HasKey("RequestId", "RetryType"); - - b.ToTable("UnacknowledgedRetryOperations"); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.FailedMessageGroupEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.FailedMessageEntity", null) - .WithMany() - .HasForeignKey("FailedMessageUniqueId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointThroughputEntity", b => - { - b.HasOne("ServiceControl.Persistence.EFCore.Entities.LicensingEndpointEntity", null) - .WithMany() - .HasForeignKey("NormalizedName", "ThroughputSource") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs deleted file mode 100644 index fcaa5cfcdc..0000000000 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911010140_AddStatusLastTimeOfFailureIndex.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations -{ - /// - public partial class AddStatusLastTimeOfFailureIndex : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateIndex( - name: "IX_FailedMessages_Status_LastTimeOfFailure", - table: "FailedMessages", - columns: new[] { "Status", "LastTimeOfFailure" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_FailedMessages_Status_LastTimeOfFailure", - table: "FailedMessages"); - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs deleted file mode 100644 index bf1edddb9c..0000000000 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations -{ - /// - public partial class AddStatusMessageTypeIndex : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.AlterColumn( - name: "MessageType", - table: "FailedMessages", - type: "nvarchar(450)", - maxLength: 450, - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(max)", - oldNullable: true); - - //normally this would be potentially slow and necessitate executing outside of the - //migration transaction, but no customers consume this persister yet so this can - //treated as an initial migration - migrationBuilder.CreateIndex( - name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", - table: "FailedMessages", - columns: new[] { "Status", "MessageType", "UniqueMessageId" }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropIndex( - name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", - table: "FailedMessages"); - - migrationBuilder.AlterColumn( - name: "MessageType", - table: "FailedMessages", - type: "nvarchar(max)", - nullable: true, - oldClrType: typeof(string), - oldType: "nvarchar(450)", - oldMaxLength: 450, - oldNullable: true); - } - } -} diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.Designer.cs similarity index 99% rename from src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs rename to src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.Designer.cs index aa505f5e4f..6b01d4d06c 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911031929_AddStatusMessageTypeIndex.Designer.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.Designer.cs @@ -12,8 +12,8 @@ namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations { [DbContext(typeof(SqlServerServiceControlDbContext))] - [Migration("20260911031929_AddStatusMessageTypeIndex")] - partial class AddStatusMessageTypeIndex + [Migration("20260911041253_AddMissingIndexes")] + partial class AddMissingIndexes { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.cs similarity index 52% rename from src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs rename to src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.cs index a41691c147..8d438de042 100644 --- a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260910054547_WidenGroupAggregateIndexes.cs +++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260911041253_AddMissingIndexes.cs @@ -5,7 +5,7 @@ namespace ServiceControl.Persistence.EFCore.SqlServer.Migrations { /// - public partial class WidenGroupAggregateIndexes : Migration + public partial class AddMissingIndexes : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -18,12 +18,35 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_FailedMessageGroups_Type_GroupId", table: "FailedMessageGroups"); + migrationBuilder.AlterColumn( + name: "MessageType", + table: "FailedMessages", + type: "nvarchar(450)", + maxLength: 450, + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(max)", + oldNullable: true); + migrationBuilder.CreateIndex( name: "IX_FailedMessages_Status_LastModified", table: "FailedMessages", columns: new[] { "Status", "LastModified" }) .Annotation("SqlServer:Include", new[] { "FirstTimeOfFailure", "LastTimeOfFailure" }); + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_LastTimeOfFailure", + table: "FailedMessages", + columns: new[] { "Status", "LastTimeOfFailure" }); + + // Normally this would be potentially slow and necessitate executing outside of the + // migration transaction, but no customers consume this persister yet so this can + // be treated as an initial migration + migrationBuilder.CreateIndex( + name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", + table: "FailedMessages", + columns: new[] { "Status", "MessageType", "UniqueMessageId" }); + migrationBuilder.CreateIndex( name: "IX_FailedMessageGroups_Type_GroupId", table: "FailedMessageGroups", @@ -38,10 +61,28 @@ protected override void Down(MigrationBuilder migrationBuilder) name: "IX_FailedMessages_Status_LastModified", table: "FailedMessages"); + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_LastTimeOfFailure", + table: "FailedMessages"); + + migrationBuilder.DropIndex( + name: "IX_FailedMessages_Status_MessageType_UniqueMessageId", + table: "FailedMessages"); + migrationBuilder.DropIndex( name: "IX_FailedMessageGroups_Type_GroupId", table: "FailedMessageGroups"); + migrationBuilder.AlterColumn( + name: "MessageType", + table: "FailedMessages", + type: "nvarchar(max)", + nullable: true, + oldClrType: typeof(string), + oldType: "nvarchar(450)", + oldMaxLength: 450, + oldNullable: true); + migrationBuilder.CreateIndex( name: "IX_FailedMessages_Status_LastModified", table: "FailedMessages",