Skip to content

Commit 4879dea

Browse files
committed
More
1 parent 3cd2daf commit 4879dea

File tree

46 files changed

+2376
-1513
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2376
-1513
lines changed

src/ServiceControl.Persistence.Sql.Core/DbContexts/ServiceControlDbContextBase.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ protected ServiceControlDbContextBase(DbContextOptions options) : base(options)
1919
public DbSet<KnownEndpointEntity> KnownEndpoints { get; set; }
2020
public DbSet<CustomCheckEntity> CustomChecks { get; set; }
2121
public DbSet<MessageBodyEntity> MessageBodies { get; set; }
22+
public DbSet<RetryHistoryEntity> RetryHistory { get; set; }
23+
public DbSet<FailedErrorImportEntity> FailedErrorImports { get; set; }
24+
public DbSet<ExternalIntegrationDispatchRequestEntity> ExternalIntegrationDispatchRequests { get; set; }
25+
public DbSet<ArchiveOperationEntity> ArchiveOperations { get; set; }
2226

2327
protected override void OnModelCreating(ModelBuilder modelBuilder)
2428
{
@@ -33,6 +37,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
3337
modelBuilder.ApplyConfiguration(new KnownEndpointConfiguration());
3438
modelBuilder.ApplyConfiguration(new CustomCheckConfiguration());
3539
modelBuilder.ApplyConfiguration(new MessageBodyConfiguration());
40+
modelBuilder.ApplyConfiguration(new RetryHistoryConfiguration());
41+
modelBuilder.ApplyConfiguration(new FailedErrorImportConfiguration());
42+
modelBuilder.ApplyConfiguration(new ExternalIntegrationDispatchRequestConfiguration());
43+
modelBuilder.ApplyConfiguration(new ArchiveOperationConfiguration());
3644

3745
OnModelCreatingProvider(modelBuilder);
3846
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace ServiceControl.Persistence.Sql.Core.Entities;
2+
3+
using System;
4+
5+
public class ArchiveOperationEntity
6+
{
7+
public string Id { get; set; } = null!; // Format: "{ArchiveType}/{RequestId}"
8+
public string RequestId { get; set; } = null!;
9+
public string GroupName { get; set; } = null!;
10+
public int ArchiveType { get; set; } // ArchiveType enum as int
11+
public int ArchiveState { get; set; } // ArchiveState enum as int
12+
public int TotalNumberOfMessages { get; set; }
13+
public int NumberOfMessagesArchived { get; set; }
14+
public int NumberOfBatches { get; set; }
15+
public int CurrentBatch { get; set; }
16+
public DateTime Started { get; set; }
17+
public DateTime? Last { get; set; }
18+
public DateTime? CompletionTime { get; set; }
19+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace ServiceControl.Persistence.Sql.Core.Entities;
2+
3+
using System;
4+
5+
public class ExternalIntegrationDispatchRequestEntity
6+
{
7+
public string Id { get; set; } = null!;
8+
public string DispatchContextJson { get; set; } = null!; // Serialize the DispatchContext object as JSON
9+
public DateTime CreatedAt { get; set; }
10+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ServiceControl.Persistence.Sql.Core.Entities;
2+
3+
public class FailedErrorImportEntity
4+
{
5+
public string Id { get; set; } = null!;
6+
public string MessageJson { get; set; } = null!; // FailedTransportMessage as JSON
7+
public string? ExceptionInfo { get; set; }
8+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace ServiceControl.Persistence.Sql.Core.Entities;
2+
3+
public class RetryHistoryEntity
4+
{
5+
public int Id { get; set; } = 1; // Singleton pattern
6+
public string? HistoricOperationsJson { get; set; }
7+
public string? UnacknowledgedOperationsJson { get; set; }
8+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
namespace ServiceControl.Persistence.Sql.Core.EntityConfigurations;
2+
3+
using Entities;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
6+
7+
class ArchiveOperationConfiguration : IEntityTypeConfiguration<ArchiveOperationEntity>
8+
{
9+
public void Configure(EntityTypeBuilder<ArchiveOperationEntity> builder)
10+
{
11+
builder.ToTable("ArchiveOperations");
12+
builder.HasKey(e => e.Id);
13+
builder.Property(e => e.Id).HasMaxLength(300).IsRequired();
14+
builder.Property(e => e.RequestId).HasMaxLength(200).IsRequired();
15+
builder.Property(e => e.GroupName).HasMaxLength(200).IsRequired();
16+
builder.Property(e => e.ArchiveType).IsRequired();
17+
builder.Property(e => e.ArchiveState).IsRequired();
18+
builder.Property(e => e.TotalNumberOfMessages).IsRequired();
19+
builder.Property(e => e.NumberOfMessagesArchived).IsRequired();
20+
builder.Property(e => e.NumberOfBatches).IsRequired();
21+
builder.Property(e => e.CurrentBatch).IsRequired();
22+
builder.Property(e => e.Started).IsRequired();
23+
builder.Property(e => e.Last);
24+
builder.Property(e => e.CompletionTime);
25+
26+
builder.HasIndex(e => e.RequestId);
27+
builder.HasIndex(e => e.ArchiveState);
28+
}
29+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace ServiceControl.Persistence.Sql.Core.EntityConfigurations;
2+
3+
using Entities;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
6+
7+
class ExternalIntegrationDispatchRequestConfiguration : IEntityTypeConfiguration<ExternalIntegrationDispatchRequestEntity>
8+
{
9+
public void Configure(EntityTypeBuilder<ExternalIntegrationDispatchRequestEntity> builder)
10+
{
11+
builder.ToTable("ExternalIntegrationDispatchRequests");
12+
builder.HasKey(e => e.Id);
13+
builder.Property(e => e.Id).HasMaxLength(200).IsRequired();
14+
builder.Property(e => e.DispatchContextJson).IsRequired();
15+
builder.Property(e => e.CreatedAt).IsRequired();
16+
17+
builder.HasIndex(e => e.CreatedAt);
18+
}
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace ServiceControl.Persistence.Sql.Core.EntityConfigurations;
2+
3+
using Entities;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
6+
7+
class FailedErrorImportConfiguration : IEntityTypeConfiguration<FailedErrorImportEntity>
8+
{
9+
public void Configure(EntityTypeBuilder<FailedErrorImportEntity> builder)
10+
{
11+
builder.ToTable("FailedErrorImports");
12+
builder.HasKey(e => e.Id);
13+
builder.Property(e => e.Id).HasMaxLength(200).IsRequired();
14+
builder.Property(e => e.MessageJson).IsRequired();
15+
builder.Property(e => e.ExceptionInfo);
16+
}
17+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace ServiceControl.Persistence.Sql.Core.EntityConfigurations;
2+
3+
using Entities;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
6+
7+
class RetryHistoryConfiguration : IEntityTypeConfiguration<RetryHistoryEntity>
8+
{
9+
public void Configure(EntityTypeBuilder<RetryHistoryEntity> builder)
10+
{
11+
builder.ToTable("RetryHistory");
12+
builder.HasKey(e => e.Id);
13+
builder.Property(e => e.Id).HasDefaultValue(1).ValueGeneratedNever();
14+
builder.Property(e => e.HistoricOperationsJson);
15+
builder.Property(e => e.UnacknowledgedOperationsJson);
16+
}
17+
}
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
namespace ServiceControl.Persistence.Sql.Core.Implementation;
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using DbContexts;
8+
using Entities;
9+
using Microsoft.EntityFrameworkCore;
10+
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Logging;
12+
using ServiceControl.Infrastructure.DomainEvents;
13+
using ServiceControl.Persistence.Recoverability;
14+
using ServiceControl.Recoverability;
15+
16+
public class ArchiveMessages : IArchiveMessages
17+
{
18+
readonly IServiceProvider serviceProvider;
19+
readonly IDomainEvents domainEvents;
20+
readonly ILogger<ArchiveMessages> logger;
21+
22+
public ArchiveMessages(
23+
IServiceProvider serviceProvider,
24+
IDomainEvents domainEvents,
25+
ILogger<ArchiveMessages> logger)
26+
{
27+
this.serviceProvider = serviceProvider;
28+
this.domainEvents = domainEvents;
29+
this.logger = logger;
30+
}
31+
32+
public async Task ArchiveAllInGroup(string groupId)
33+
{
34+
// This would update all failed messages in the group to archived status
35+
// For now, this is a placeholder that would need the failed message infrastructure
36+
logger.LogInformation("Archiving all messages in group {GroupId}", groupId);
37+
await Task.CompletedTask;
38+
}
39+
40+
public async Task UnarchiveAllInGroup(string groupId)
41+
{
42+
logger.LogInformation("Unarchiving all messages in group {GroupId}", groupId);
43+
await Task.CompletedTask;
44+
}
45+
46+
public bool IsOperationInProgressFor(string groupId, ArchiveType archiveType)
47+
{
48+
using var scope = serviceProvider.CreateScope();
49+
var dbContext = scope.ServiceProvider.GetRequiredService<ServiceControlDbContextBase>();
50+
51+
var operationId = MakeOperationId(groupId, archiveType);
52+
var operation = dbContext.ArchiveOperations
53+
.AsNoTracking()
54+
.FirstOrDefault(a => a.Id == operationId);
55+
56+
if (operation == null)
57+
{
58+
return false;
59+
}
60+
61+
return operation.ArchiveState != (int)ArchiveState.ArchiveCompleted;
62+
}
63+
64+
public bool IsArchiveInProgressFor(string groupId)
65+
{
66+
return IsOperationInProgressFor(groupId, ArchiveType.FailureGroup) ||
67+
IsOperationInProgressFor(groupId, ArchiveType.All);
68+
}
69+
70+
public void DismissArchiveOperation(string groupId, ArchiveType archiveType)
71+
{
72+
using var scope = serviceProvider.CreateScope();
73+
var dbContext = scope.ServiceProvider.GetRequiredService<ServiceControlDbContextBase>();
74+
75+
var operationId = MakeOperationId(groupId, archiveType);
76+
var operation = dbContext.ArchiveOperations.FirstOrDefault(a => a.Id == operationId);
77+
78+
if (operation != null)
79+
{
80+
dbContext.ArchiveOperations.Remove(operation);
81+
dbContext.SaveChanges();
82+
}
83+
}
84+
85+
public async Task StartArchiving(string groupId, ArchiveType archiveType)
86+
{
87+
using var scope = serviceProvider.CreateScope();
88+
var dbContext = scope.ServiceProvider.GetRequiredService<ServiceControlDbContextBase>();
89+
90+
var operation = new ArchiveOperationEntity
91+
{
92+
Id = MakeOperationId(groupId, archiveType),
93+
RequestId = groupId,
94+
GroupName = groupId,
95+
ArchiveType = (int)archiveType,
96+
ArchiveState = (int)ArchiveState.ArchiveStarted,
97+
TotalNumberOfMessages = 0,
98+
NumberOfMessagesArchived = 0,
99+
NumberOfBatches = 0,
100+
CurrentBatch = 0,
101+
Started = DateTime.UtcNow
102+
};
103+
104+
await dbContext.ArchiveOperations.AddAsync(operation);
105+
await dbContext.SaveChangesAsync();
106+
107+
logger.LogInformation("Started archiving for group {GroupId}", groupId);
108+
}
109+
110+
public async Task StartUnarchiving(string groupId, ArchiveType archiveType)
111+
{
112+
using var scope = serviceProvider.CreateScope();
113+
var dbContext = scope.ServiceProvider.GetRequiredService<ServiceControlDbContextBase>();
114+
115+
var operation = new ArchiveOperationEntity
116+
{
117+
Id = MakeOperationId(groupId, archiveType),
118+
RequestId = groupId,
119+
GroupName = groupId,
120+
ArchiveType = (int)archiveType,
121+
ArchiveState = (int)ArchiveState.ArchiveStarted,
122+
TotalNumberOfMessages = 0,
123+
NumberOfMessagesArchived = 0,
124+
NumberOfBatches = 0,
125+
CurrentBatch = 0,
126+
Started = DateTime.UtcNow
127+
};
128+
129+
await dbContext.ArchiveOperations.AddAsync(operation);
130+
await dbContext.SaveChangesAsync();
131+
132+
logger.LogInformation("Started unarchiving for group {GroupId}", groupId);
133+
}
134+
135+
public IEnumerable<InMemoryArchive> GetArchivalOperations()
136+
{
137+
using var scope = serviceProvider.CreateScope();
138+
var dbContext = scope.ServiceProvider.GetRequiredService<ServiceControlDbContextBase>();
139+
140+
var operations = dbContext.ArchiveOperations
141+
.AsNoTracking()
142+
.ToList();
143+
144+
return operations.Select(op => new InMemoryArchive(op.RequestId, (ArchiveType)op.ArchiveType, domainEvents)
145+
{
146+
GroupName = op.GroupName,
147+
ArchiveState = (ArchiveState)op.ArchiveState,
148+
TotalNumberOfMessages = op.TotalNumberOfMessages,
149+
NumberOfMessagesArchived = op.NumberOfMessagesArchived,
150+
NumberOfBatches = op.NumberOfBatches,
151+
CurrentBatch = op.CurrentBatch,
152+
Started = op.Started,
153+
Last = op.Last,
154+
CompletionTime = op.CompletionTime
155+
}).ToList();
156+
}
157+
158+
static string MakeOperationId(string groupId, ArchiveType archiveType)
159+
{
160+
return $"{archiveType}/{groupId}";
161+
}
162+
}

0 commit comments

Comments
 (0)