test: create test for concurrency

This commit is contained in:
voltsrage
2026-06-23 05:17:22 +08:00
parent 383df0dde5
commit d8e142fffe
8 changed files with 2625 additions and 58 deletions
@@ -20,7 +20,7 @@ public class SepsisBundleService : ISepsisBundleService
if (alertType != AlertType.SofaSepsis)
throw new InvalidOperationException(
$"Sepsis bundle can only be triggered by SOFA_SEPSIS, not {alertType.ToDbString()}.");
var exists = await _db.SepsisBundles
.AnyAsync(b => b.EncounterId == encounterId
&& b.ComplianceStatus == SepsisBundleComplianceStatus.InProgress, ct);
@@ -28,73 +28,75 @@ public class SepsisBundleService : ISepsisBundleService
if (exists)
return null;
var recognizedAt = DateTimeOffset.UtcNow;
var bundle = new SepsisBundle
{
Id = Guid.NewGuid(),
EncounterId = encounterId,
TriggeringAlertId = triggeringAlertId,
TriggeringAlertType = alertType.ToDbString(),
RecognizedAt = recognizedAt,
DeadlineAt = recognizedAt.AddHours(1),
ComplianceStatus = SepsisBundleComplianceStatus.InProgress
};
var elementDefs = new (SepsisBundleElementType Type, OrderType OrderType, string Description)[]
{
(SepsisBundleElementType.BloodCultures, OrderType.Lab, "SEP-1: Blood cultures"),
(SepsisBundleElementType.SerumLactate, OrderType.Lab, "SEP-1: Serum lactate"),
(SepsisBundleElementType.BroadSpectrumAntibiotics, OrderType.Medication, "SEP-1: Broad-spectrum antibiotics"),
(SepsisBundleElementType.IvFluidResuscitation, OrderType.Procedure, "SEP-1: IV fluid bolus"),
};
foreach (var (elementType, orderType, description) in elementDefs)
{
var order = await _orderService.CreateAsync(encounterId, new CreateOrderRequest(
orderType, description, "sepsis-bundle-engine"));
bundle.Elements.Add(new SepsisBundleElement
{
Id = Guid.NewGuid(),
ElementType = elementType,
Status = SepsisBundleElementStatus.Pending,
OrderId = order.Id
});
}
_db.SepsisBundles.Add(bundle);
_db.OutboxEvents.Add(new OutboxEvent
{
Id = Guid.NewGuid(),
Topic = "sepsis.bundle.created",
Payload = JsonSerializer.Serialize(new
{
bundleId = bundle.Id,
encounterId,
triggeringAlertId,
triggeringAlertType = alertType.ToDbString(),
recognizedAt,
deadlineAt = bundle.DeadlineAt,
partitionKey = encounterId.ToString()
}),
PartitionKey = encounterId.ToString(),
CreatedAt = DateTimeOffset.UtcNow
});
await using var tx = await _db.Database.BeginTransactionAsync(ct);
try
{
var recognizedAt = DateTimeOffset.UtcNow;
var bundle = new SepsisBundle
{
Id = Guid.NewGuid(),
EncounterId = encounterId,
TriggeringAlertId = triggeringAlertId,
TriggeringAlertType = alertType.ToDbString(),
RecognizedAt = recognizedAt,
DeadlineAt = recognizedAt.AddHours(1),
ComplianceStatus = SepsisBundleComplianceStatus.InProgress
};
var elementDefs = new (SepsisBundleElementType Type, OrderType OrderType, string Description)[]
{
(SepsisBundleElementType.BloodCultures, OrderType.Lab, "SEP-1: Blood cultures"),
(SepsisBundleElementType.SerumLactate, OrderType.Lab, "SEP-1: Serum lactate"),
(SepsisBundleElementType.BroadSpectrumAntibiotics, OrderType.Medication, "SEP-1: Broad-spectrum antibiotics"),
(SepsisBundleElementType.IvFluidResuscitation, OrderType.Procedure, "SEP-1: IV fluid bolus"),
};
foreach (var (elementType, orderType, description) in elementDefs)
{
var order = await _orderService.CreateAsync(encounterId, new CreateOrderRequest(
orderType, description, "sepsis-bundle-engine"));
bundle.Elements.Add(new SepsisBundleElement
{
Id = Guid.NewGuid(),
ElementType = elementType,
Status = SepsisBundleElementStatus.Pending,
OrderId = order.Id
});
}
_db.SepsisBundles.Add(bundle);
_db.OutboxEvents.Add(new OutboxEvent
{
Id = Guid.NewGuid(),
Topic = "sepsis.bundle.created",
Payload = JsonSerializer.Serialize(new
{
bundleId = bundle.Id,
encounterId,
triggeringAlertId,
triggeringAlertType = alertType.ToDbString(),
recognizedAt,
deadlineAt = bundle.DeadlineAt,
partitionKey = encounterId.ToString()
}),
PartitionKey = encounterId.ToString(),
CreatedAt = DateTimeOffset.UtcNow
});
await _db.SaveChangesAsync(ct);
await tx.CommitAsync(ct);
return bundle;
}
catch (DbUpdateException ex)
when (ex.InnerException is Npgsql.PostgresException { SqlState: "23505" } pg
&& pg.ConstraintName == "ix_sepsis_bundles_encounter_in_progress")
{
await tx.RollbackAsync(ct);
_db.ChangeTracker.Clear();
return null;
}
return bundle;
}
public async Task<SepsisBundle?> GetCurrentByEncounterAsync(Guid encounterId)