diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.Designer.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.Designer.cs
new file mode 100644
index 0000000000..964f060ceb
--- /dev/null
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.Designer.cs
@@ -0,0 +1,568 @@
+//
+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("20260803021015_AddRetryBatches")]
+ partial class AddRetryBatches
+ {
+ ///
+ 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.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("EventLogItems", (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.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")
+ .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");
+
+ 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");
+
+ 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.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.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.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.TrialMetadataEntity", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer")
+ .HasColumnName("id");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("TrialEndDate")
+ .HasColumnType("date")
+ .HasColumnName("trial_end_date");
+
+ b.HasKey("Id")
+ .HasName("pk_trial_metadata");
+
+ b.ToTable("trial_metadata", (string)null);
+
+ b.HasData(
+ new
+ {
+ Id = 1
+ });
+ });
+
+ 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");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.cs
new file mode 100644
index 0000000000..13fcc9c8ae
--- /dev/null
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/20260803021015_AddRetryBatches.cs
@@ -0,0 +1,109 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace ServiceControl.Persistence.EFCore.PostgreSql.Migrations
+{
+ ///
+ public partial class AddRetryBatches : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "retry_id",
+ table: "failed_message_retries");
+
+ migrationBuilder.AddColumn(
+ name: "retry_batch_id",
+ table: "failed_message_retries",
+ type: "uuid",
+ nullable: false,
+ defaultValue: new Guid("00000000-0000-0000-0000-000000000000"));
+
+ migrationBuilder.AddColumn(
+ name: "stage_attempts",
+ table: "failed_message_retries",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.CreateTable(
+ name: "retry_batch_now_forwarding",
+ columns: table => new
+ {
+ id = table.Column(type: "integer", nullable: false),
+ retry_batch_id = table.Column(type: "uuid", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_retry_batch_now_forwarding", x => x.id);
+ });
+
+ migrationBuilder.CreateTable(
+ name: "retry_batches",
+ columns: table => new
+ {
+ id = table.Column(type: "uuid", nullable: false),
+ status = table.Column(type: "integer", nullable: false),
+ retry_session_id = table.Column(type: "character varying(450)", maxLength: 450, nullable: false),
+ request_id = table.Column(type: "character varying(450)", maxLength: 450, nullable: false),
+ retry_type = table.Column(type: "integer", nullable: false),
+ initial_batch_size = table.Column(type: "integer", nullable: false),
+ start_time = table.Column(type: "timestamp with time zone", nullable: false),
+ last = table.Column(type: "timestamp with time zone", nullable: true),
+ staging_id = table.Column(type: "character varying(450)", maxLength: 450, nullable: true),
+ context = table.Column(type: "text", nullable: true),
+ originator = table.Column(type: "text", nullable: true),
+ classifier = table.Column(type: "text", nullable: true),
+ initiated_by_id = table.Column(type: "character varying(450)", maxLength: 450, nullable: true),
+ initiated_by_name = table.Column(type: "text", nullable: true),
+ operation_id = table.Column(type: "character varying(450)", maxLength: 450, nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("pk_retry_batches", x => x.id);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "ix_failed_message_retries_retry_batch_id",
+ table: "failed_message_retries",
+ column: "retry_batch_id");
+
+ migrationBuilder.CreateIndex(
+ name: "ix_retry_batches_status_retry_session_id",
+ table: "retry_batches",
+ columns: new[] { "status", "retry_session_id" });
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "retry_batch_now_forwarding");
+
+ migrationBuilder.DropTable(
+ name: "retry_batches");
+
+ migrationBuilder.DropIndex(
+ name: "ix_failed_message_retries_retry_batch_id",
+ table: "failed_message_retries");
+
+ migrationBuilder.DropColumn(
+ name: "retry_batch_id",
+ table: "failed_message_retries");
+
+ migrationBuilder.DropColumn(
+ name: "stage_attempts",
+ table: "failed_message_retries");
+
+ migrationBuilder.AddColumn(
+ name: "retry_id",
+ table: "failed_message_retries",
+ type: "character varying(450)",
+ maxLength: 450,
+ nullable: true);
+ }
+ }
+}
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
index 6feea9b8a8..8184cac7ba 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/Migrations/PostgreSqlServiceControlDbContextModelSnapshot.cs
@@ -371,14 +371,20 @@ protected override void BuildModel(ModelBuilder modelBuilder)
.HasColumnType("uuid")
.HasColumnName("unique_message_id");
- b.Property("RetryId")
- .HasMaxLength(450)
- .HasColumnType("character varying(450)")
- .HasColumnName("retry_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);
});
@@ -455,6 +461,100 @@ protected override void BuildModel(ModelBuilder modelBuilder)
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.SubscriptionEntity", b =>
{
b.Property("MessageType")
diff --git a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlIngestionSqlDialect.cs b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlIngestionSqlDialect.cs
index 6d57b747f9..3a0bdedafe 100644
--- a/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlIngestionSqlDialect.cs
+++ b/src/ServiceControl.Persistence.EFCore.PostgreSql/PostgreSqlIngestionSqlDialect.cs
@@ -65,6 +65,23 @@ ON CONFLICT (id) DO NOTHING
}
}
+ public async Task InsertMissingRetryClaims(ServiceControlDbContext dbContext, IReadOnlyList rows, CancellationToken cancellationToken)
+ {
+ foreach (var chunk in rows.Chunk(MaxRowsPerStatement))
+ {
+ await Execute(
+ dbContext,
+ $"""
+ INSERT INTO failed_message_retries (unique_message_id, retry_batch_id, stage_attempts)
+ VALUES
+ {ParameterRows(chunk.Length, 3)}
+ ON CONFLICT (unique_message_id) DO NOTHING
+ """,
+ chunk.Select(retry => new object?[] { retry.UniqueMessageId, retry.RetryBatchId, retry.StageAttempts }),
+ cancellationToken);
+ }
+ }
+
static async Task Execute(ServiceControlDbContext dbContext, string sql, IEnumerable