fix(aihit-sync): Оптимизация GetDeltaAsync и атомарность сохранения
- GetDeltaAsync: фильтрация по relevantParentNames вместо выгрузки всех связей из БД - SyncAsync: атомарность через ChangeTracker + CommitAsync без явной транзакции
This commit is contained in:
@@ -39,21 +39,11 @@ internal class RelationshipsSyncService : IRelationshipsSyncService
|
||||
|
||||
var (toRemove, toAdd) = await GetDeltaAsync(validPairs);
|
||||
|
||||
// Транзакция гарантирует атомарность зеркалирования.
|
||||
// Без неё частичный сбой оставляет БД в состоянии, не совпадающем со снимком.
|
||||
await using var transaction = await _unitRepository.BeginTransactionAsync();
|
||||
try
|
||||
{
|
||||
await RemoveRelationshipsAsync(toRemove);
|
||||
await AddRelationshipsAsync(toAdd, unitsByName);
|
||||
await CommitChangesAsync(toRemove.Count, toAdd.Count);
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
throw;
|
||||
}
|
||||
// Удаление и добавление накапливаются в одном ChangeTracker.
|
||||
// CommitAsync сохраняет всё атомарно через SaveChangesAsync.
|
||||
await RemoveRelationshipsAsync(toRemove);
|
||||
await AddRelationshipsAsync(toAdd, unitsByName);
|
||||
await CommitChangesAsync(toRemove.Count, toAdd.Count);
|
||||
}
|
||||
|
||||
private List<(string ParentName, string ChildName)> ValidateInput(List<AihitData> data)
|
||||
@@ -128,32 +118,38 @@ internal class RelationshipsSyncService : IRelationshipsSyncService
|
||||
|
||||
_logger.LogInformation("Создание отсутствующих юнитов: {Count}", missingNames.Count);
|
||||
|
||||
var newUnits = missingNames.Select(name => new Unit
|
||||
foreach (var name in missingNames)
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = name,
|
||||
DateCreated = DateTimeOffset.UtcNow
|
||||
}).ToList();
|
||||
var unit = new Unit
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
Name = name,
|
||||
DateCreated = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
_unitRepository.CreateRange(newUnits);
|
||||
await _unitRepository.CommitAsync();
|
||||
if (!await _unitRepository.CreateAsync(unit))
|
||||
{
|
||||
_logger.LogError("Не удалось создать юнит: {Name}", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (var unit in newUnits)
|
||||
{
|
||||
existingUnits[unit.Name] = unit;
|
||||
_logger.LogDebug("Создан юнит: {Name}", unit.Name);
|
||||
existingUnits[name] = unit;
|
||||
_logger.LogDebug("Создан юнит: {Name}", name);
|
||||
}
|
||||
|
||||
await _unitRepository.CommitAsync();
|
||||
return existingUnits;
|
||||
}
|
||||
|
||||
private async Task<(List<ExistingRelationship> ToRemove, List<(string ParentName, string ChildName)> ToAdd)> GetDeltaAsync(
|
||||
List<(string ParentName, string ChildName)> sourcePairs)
|
||||
{
|
||||
var allExisting = await _unitRepository.Get()
|
||||
var relevantParentNames = sourcePairs.Select(p => p.ParentName).Distinct().ToList();
|
||||
|
||||
var existingForParents = await _unitRepository.Get()
|
||||
.AsNoTracking()
|
||||
.Include(u => u.ChildUnits).ThenInclude(r => r.ChildUnit)
|
||||
.Where(u => u.ChildUnits.Any())
|
||||
.Where(u => relevantParentNames.Contains(u.Name))
|
||||
.SelectMany(u => u.ChildUnits, (parent, relationship) => new ExistingRelationship
|
||||
{
|
||||
ParentName = parent.Name,
|
||||
@@ -161,13 +157,19 @@ internal class RelationshipsSyncService : IRelationshipsSyncService
|
||||
Relationship = relationship
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
|
||||
var sourceSet = new HashSet<(string, string)>(sourcePairs);
|
||||
var existingSet = new HashSet<(string, string)>(allExisting.Select(x => (x.ParentName, x.ChildName)));
|
||||
|
||||
var toRemove = allExisting.Where(r => !sourceSet.Contains((r.ParentName, r.ChildName))).ToList();
|
||||
var toAdd = sourcePairs.Where(p => !existingSet.Contains(p)).ToList();
|
||||
|
||||
var existingSet = new HashSet<(string, string)>(
|
||||
existingForParents.Select(x => (x.ParentName, x.ChildName)));
|
||||
|
||||
var toRemove = existingForParents
|
||||
.Where(r => !sourceSet.Contains((r.ParentName, r.ChildName)))
|
||||
.ToList();
|
||||
|
||||
var toAdd = sourcePairs
|
||||
.Where(p => !existingSet.Contains(p))
|
||||
.ToList();
|
||||
|
||||
_logger.LogInformation("Дельта: удаление {ToRemove}, добавление {ToAdd}", toRemove.Count, toAdd.Count);
|
||||
return (toRemove, toAdd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user