diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/FullTextSearchSql.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/FullTextSearchSql.cs
index 1887885ec9..1dc63f1c44 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/FullTextSearchSql.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/FullTextSearchSql.cs
@@ -3,10 +3,7 @@ namespace ServiceControl.Persistence.EFCore.PostgreSql;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
///
-/// Full text search DDL for the failed messages table. EF Core cannot model a GIN index over an
-/// expression, so it is applied by the AddFullTextSearch migration. The statements live here, and
-/// not in the migration itself, so that regenerating the migrations with the dotnet-ef CLI only
-/// costs a one line migration body.
+/// Full text search DDL for the failed messages table. EF Core cannot model a GIN index over an expression, so migrations apply it as SQL: AddFullTextSearch created the index, and WidenMessageIdAndCapFullTextBody replaced it with one that indexes only the start of the body. The statements live here, and not in the migrations themselves, so that regenerating the migrations with the dotnet-ef CLI only costs a one line migration body.
///
static class FullTextSearchSql
{
@@ -17,6 +14,9 @@ static class FullTextSearchSql
// stopword removal do more harm than good.
public const string Configuration = "simple";
+ // to_tsvector fails once the vector it builds passes 1 MB, and the index builds one on every insert, so a body with enough distinct words would fail its whole ingestion batch. Only the start of the body is indexed, which keeps the vector well under that limit.
+ public const int IndexedBodyLength = 262_144;
+
// Written the way PostgreSqlFullTextSearchDialect makes EF Core render it, down to the casing
// and the redundant looking parentheses: PostgreSQL only uses an expression index when the
// query expression parses to the same tree, and a mismatch downgrades search to a sequential
@@ -26,12 +26,18 @@ static class FullTextSearchSql
// "ServiceControl.MessageFailures.MyMessage" would not otherwise match a search for
// "MyMessage". It mirrors the SearchableMessageType that MessageTypeEnricher produces for
// RavenDB, and is not the duplicate of the headers it looks like.
- public const string IndexedExpression =
+ public static readonly string IndexedExpression =
+ $"""to_tsvector('{Configuration}', headers_json || ' ' || substring(COALESCE(body_text, ''), 1, {IndexedBodyLength}) || ' ' || replace(replace(COALESCE(message_type, ''), '.', ' '), '+', ' '))""";
+
+ // What AddFullTextSearch indexed. WidenMessageIdAndCapFullTextBody replaces it on the way up and restores it on the way down.
+ const string UncappedIndexedExpression =
$"""to_tsvector('{Configuration}', headers_json || ' ' || COALESCE(body_text, '') || ' ' || replace(replace(COALESCE(message_type, ''), '.', ' '), '+', ' '))""";
- public static readonly string Up = CreateIndexSql(null);
+ public static readonly string CreateUncappedIndex = CreateIndexSql(null, UncappedIndexedExpression);
+
+ public static readonly string CreateIndex = CreateIndexSql(null, IndexedExpression);
- public static readonly string Down = DropIndexSql(null);
+ public static readonly string DropIndex = DropIndexSql(null);
///
/// Re-renders the statement the migration carries, this time with the configured schema in it.
@@ -42,15 +48,16 @@ static class FullTextSearchSql
public static MigrationOperation Rewrite(SqlOperation operation, string schema) =>
operation.Sql switch
{
- var sql when sql == Up => WithSql(operation, CreateIndexSql(schema)),
- var sql when sql == Down => WithSql(operation, DropIndexSql(schema)),
+ var sql when sql == CreateUncappedIndex => WithSql(operation, CreateIndexSql(schema, UncappedIndexedExpression)),
+ var sql when sql == CreateIndex => WithSql(operation, CreateIndexSql(schema, IndexedExpression)),
+ var sql when sql == DropIndex => WithSql(operation, DropIndexSql(schema)),
_ => operation
};
- public static bool IsHandled(string sql) => sql == Up || sql == Down;
+ public static bool IsHandled(string sql) => sql == CreateUncappedIndex || sql == CreateIndex || sql == DropIndex;
- static string CreateIndexSql(string? schema) =>
- $"CREATE INDEX {IndexName} ON {Qualify(schema, TableName)} USING GIN ({IndexedExpression})";
+ static string CreateIndexSql(string? schema, string indexedExpression) =>
+ $"CREATE INDEX {IndexName} ON {Qualify(schema, TableName)} USING GIN ({indexedExpression})";
// An index belongs to its table's schema, so it is the index that gets qualified here.
static string DropIndexSql(string? schema) => $"DROP INDEX IF EXISTS {Qualify(schema, IndexName)}";
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260902040422_AddFullTextSearch.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260902040422_AddFullTextSearch.cs
index dfcd89e387..e2515b3234 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260902040422_AddFullTextSearch.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260902040422_AddFullTextSearch.cs
@@ -8,9 +8,9 @@ namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations
public partial class AddFullTextSearch : Migration
{
///
- protected override void Up(MigrationBuilder migrationBuilder) => migrationBuilder.Sql(FullTextSearchSql.Up);
+ protected override void Up(MigrationBuilder migrationBuilder) => migrationBuilder.Sql(FullTextSearchSql.CreateUncappedIndex);
///
- protected override void Down(MigrationBuilder migrationBuilder) => migrationBuilder.Sql(FullTextSearchSql.Down);
+ protected override void Down(MigrationBuilder migrationBuilder) => migrationBuilder.Sql(FullTextSearchSql.DropIndex);
}
}
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260928064706_WidenMessageIdAndCapFullTextBody.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260928064706_WidenMessageIdAndCapFullTextBody.Designer.cs
new file mode 100644
index 0000000000..ace61e5c47
--- /dev/null
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260928064706_WidenMessageIdAndCapFullTextBody.Designer.cs
@@ -0,0 +1,923 @@
+//
+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("20260928064706_WidenMessageIdAndCapFullTextBody")]
+ partial class WidenMessageIdAndCapFullTextBody
+ {
+ ///
+ 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()
+ .HasColumnType("text")
+ .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")
+ .HasColumnType("text")
+ .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", "LastTimeOfFailure")
+ .HasDatabaseName("ix_failed_messages_status_last_time_of_failure");
+
+ b.HasIndex("Status", "LastModified", "UniqueMessageId")
+ .HasDatabaseName("ix_failed_messages_status_last_modified_unique_message_id");
+
+ NpgsqlIndexBuilderExtensions.IncludeProperties(b.HasIndex("Status", "LastModified", "UniqueMessageId"), new[] { "FirstTimeOfFailure", "LastTimeOfFailure" });
+
+ 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[] { "FailedMessageUniqueId", "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/20260928064706_WidenMessageIdAndCapFullTextBody.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260928064706_WidenMessageIdAndCapFullTextBody.cs
new file mode 100644
index 0000000000..1f1f47fc20
--- /dev/null
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260928064706_WidenMessageIdAndCapFullTextBody.cs
@@ -0,0 +1,62 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations
+{
+ ///
+ public partial class WidenMessageIdAndCapFullTextBody : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AlterColumn(
+ name: "message_id",
+ table: "failed_messages",
+ type: "text",
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "character varying(450)",
+ oldMaxLength: 450,
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "message_id",
+ table: "failed_error_imports",
+ type: "text",
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "character varying(450)",
+ oldMaxLength: 450);
+
+ migrationBuilder.Sql(FullTextSearchSql.DropIndex);
+ migrationBuilder.Sql(FullTextSearchSql.CreateIndex);
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.Sql(FullTextSearchSql.DropIndex);
+ migrationBuilder.Sql(FullTextSearchSql.CreateUncappedIndex);
+
+ migrationBuilder.AlterColumn(
+ name: "message_id",
+ table: "failed_messages",
+ type: "character varying(450)",
+ maxLength: 450,
+ nullable: true,
+ oldClrType: typeof(string),
+ oldType: "text",
+ oldNullable: true);
+
+ migrationBuilder.AlterColumn(
+ name: "message_id",
+ table: "failed_error_imports",
+ type: "character varying(450)",
+ maxLength: 450,
+ nullable: false,
+ oldClrType: typeof(string),
+ oldType: "text");
+ }
+ }
+}
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
index 06b1a9b639..3403a3d831 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
@@ -264,8 +264,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
b.Property("MessageId")
.IsRequired()
- .HasMaxLength(450)
- .HasColumnType("character varying(450)")
+ .HasColumnType("text")
.HasColumnName("message_id");
b.HasKey("UniqueMessageId")
@@ -366,8 +365,7 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnName("last_time_of_failure");
b.Property("MessageId")
- .HasMaxLength(450)
- .HasColumnType("character varying(450)")
+ .HasColumnType("text")
.HasColumnName("message_id");
b.Property("MessageType")
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlFullTextSearchDialect.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlFullTextSearchDialect.cs
index d2c01fb908..8461a8a12c 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlFullTextSearchDialect.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlFullTextSearchDialect.cs
@@ -15,7 +15,7 @@ public IQueryable Search(IQueryable so
source.Where(message =>
EF.Functions.ToTsVector(FullTextSearchSql.Configuration,
message.HeadersJson + " " +
- (message.BodyText ?? "") + " " +
+ (message.BodyText ?? "").Substring(0, FullTextSearchSql.IndexedBodyLength) + " " +
(message.MessageType ?? "").Replace(".", " ").Replace("+", " "))
.Matches(EF.Functions.WebSearchToTsQuery(FullTextSearchSql.Configuration, ToOrQuery(searchTerms))));
diff --git a/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260928064709_WidenMessageId.Designer.cs b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260928064709_WidenMessageId.Designer.cs
new file mode 100644
index 0000000000..704d62a08e
--- /dev/null
+++ b/src/ServiceControl.Persistence.EFCore.SqlServer/Migrations/20260928064709_WidenMessageId.Designer.cs
@@ -0,0 +1,741 @@
+//
+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("20260928064709_WidenMessageId")]
+ partial class WidenMessageId
+ {
+ ///
+ 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()
+ .HasColumnType("nvarchar(max)");
+
+ 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")
+ .HasColumnType("nvarchar(max)");
+
+ 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