fix: run dashboard queries sequentially to avoid DbContext concurrency
DbContext is not thread-safe. Task.WhenAll caused concurrent access within the same scoped DbContext, throwing 'A second operation was started on this context instance' errors on slower machines. Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
This commit is contained in:
co-authored by
Claude Opus 4.8 noreply@anthropic.com
parent
a819322559
commit
f5ad1dec00
@@ -44,48 +44,34 @@ public sealed class DashboardService
|
|||||||
var todayUtcEnd = new DateTimeOffset(todayBeijingDate.ToDateTime(TimeOnly.MaxValue), ChinaTime.Zone.GetUtcOffset(beijingNow.DateTime));
|
var todayUtcEnd = new DateTimeOffset(todayBeijingDate.ToDateTime(TimeOnly.MaxValue), ChinaTime.Zone.GetUtcOffset(beijingNow.DateTime));
|
||||||
var recentErrorSince = now.AddHours(-24);
|
var recentErrorSince = now.AddHours(-24);
|
||||||
|
|
||||||
// Run independent queries in parallel for efficiency
|
// Run queries sequentially — DbContext is not thread-safe
|
||||||
var activeRecordingTask = _recordSessionRepository.CountByStatusAsync(RecordSessionStatus.Running, cancellationToken);
|
var activeRecordingCount = await _recordSessionRepository.CountByStatusAsync(RecordSessionStatus.Running, cancellationToken);
|
||||||
var liveRoomCountTask = _liveRoomRepository.CountByAvailabilityAsync(LiveRoomAvailabilityStatus.Live, cancellationToken);
|
var liveRoomCount = await _liveRoomRepository.CountByAvailabilityAsync(LiveRoomAvailabilityStatus.Live, cancellationToken);
|
||||||
var offlineRoomCountTask = _liveRoomRepository.CountByAvailabilityAsync(LiveRoomAvailabilityStatus.Offline, cancellationToken);
|
var offlineRoomCount = await _liveRoomRepository.CountByAvailabilityAsync(LiveRoomAvailabilityStatus.Offline, cancellationToken);
|
||||||
var totalRoomCountTask = _liveRoomRepository.CountAsync(cancellationToken);
|
var totalRoomCount = await _liveRoomRepository.CountAsync(cancellationToken);
|
||||||
var activeSessionCountTask = _recordSessionRepository.CountActiveAsync(cancellationToken);
|
var activeSessionCount = await _recordSessionRepository.CountActiveAsync(cancellationToken);
|
||||||
var recentErrorCountTask = _systemLogRepository.CountRecentErrorsAsync(recentErrorSince, cancellationToken);
|
var recentErrorCount = await _systemLogRepository.CountRecentErrorsAsync(recentErrorSince, cancellationToken);
|
||||||
var todayDurationTask = _recordTaskRepository.SumDurationSecondsAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
var todayRecordingSeconds = await _recordTaskRepository.SumDurationSecondsAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
||||||
var todayAggregateTask = _recordResultRepository.GetTodayAggregateAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
var (todayTotalBytes, todayTotalDanmaku) = await _recordResultRepository.GetTodayAggregateAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
||||||
var recentSessionsTask = _recordSessionRepository.ListRecentAsync(5, cancellationToken);
|
var recentSessions = await _recordSessionRepository.ListRecentAsync(5, cancellationToken);
|
||||||
var todaySessionsTask = _recordSessionRepository.ListInDateRangeAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
var todaySessions = await _recordSessionRepository.ListInDateRangeAsync(todayUtcStart, todayUtcEnd, cancellationToken);
|
||||||
var settingsTask = _systemSettingsService.GetAsync(cancellationToken);
|
var settings = await _systemSettingsService.GetAsync(cancellationToken);
|
||||||
|
|
||||||
await Task.WhenAll(
|
|
||||||
activeRecordingTask,
|
|
||||||
liveRoomCountTask,
|
|
||||||
offlineRoomCountTask,
|
|
||||||
totalRoomCountTask,
|
|
||||||
activeSessionCountTask,
|
|
||||||
recentErrorCountTask,
|
|
||||||
todayDurationTask,
|
|
||||||
todayAggregateTask,
|
|
||||||
recentSessionsTask,
|
|
||||||
todaySessionsTask,
|
|
||||||
(Task)settingsTask
|
|
||||||
);
|
|
||||||
|
|
||||||
var (todayTotalBytes, todayTotalDanmaku) = todayAggregateTask.Result;
|
|
||||||
var settings = settingsTask.Result;
|
|
||||||
var storageCheck = _storageGuardService.CheckCanStartOrResume(settings);
|
var storageCheck = _storageGuardService.CheckCanStartOrResume(settings);
|
||||||
|
var pendingTranscodeCount = await _recordTaskRepository.CountByStatusAsync(RecordTaskStatus.Processing, cancellationToken);
|
||||||
|
var pendingUploadCount = await _recordResultRepository.CountPendingUploadAsync(cancellationToken);
|
||||||
|
var queuedDataBytes = await _recordResultRepository.SumPendingUploadBytesAsync(cancellationToken);
|
||||||
|
|
||||||
return new DashboardDto
|
return new DashboardDto
|
||||||
{
|
{
|
||||||
ActiveRecordingCount = activeRecordingTask.Result,
|
ActiveRecordingCount = activeRecordingCount,
|
||||||
LiveRoomCount = liveRoomCountTask.Result,
|
LiveRoomCount = liveRoomCount,
|
||||||
OfflineRoomCount = offlineRoomCountTask.Result,
|
OfflineRoomCount = offlineRoomCount,
|
||||||
TotalRoomCount = totalRoomCountTask.Result,
|
TotalRoomCount = totalRoomCount,
|
||||||
TodayRecordingSeconds = todayDurationTask.Result,
|
TodayRecordingSeconds = todayRecordingSeconds,
|
||||||
TodayDataBytes = todayTotalBytes,
|
TodayDataBytes = todayTotalBytes,
|
||||||
TodayDanmakuCount = todayTotalDanmaku,
|
TodayDanmakuCount = todayTotalDanmaku,
|
||||||
ActiveSessionCount = activeSessionCountTask.Result,
|
ActiveSessionCount = activeSessionCount,
|
||||||
RecentErrorCount = recentErrorCountTask.Result,
|
RecentErrorCount = recentErrorCount,
|
||||||
StorageStatus = new StorageStatusDto
|
StorageStatus = new StorageStatusDto
|
||||||
{
|
{
|
||||||
HasEnoughSpace = storageCheck.HasEnoughSpace,
|
HasEnoughSpace = storageCheck.HasEnoughSpace,
|
||||||
@@ -94,13 +80,13 @@ public sealed class DashboardService
|
|||||||
Tier = storageCheck.Tier.ToString(),
|
Tier = storageCheck.Tier.ToString(),
|
||||||
UsagePercent = storageCheck.UsagePercent
|
UsagePercent = storageCheck.UsagePercent
|
||||||
},
|
},
|
||||||
PendingTranscodeCount = await _recordTaskRepository.CountByStatusAsync(Domain.Enums.RecordTaskStatus.Processing, cancellationToken),
|
PendingTranscodeCount = pendingTranscodeCount,
|
||||||
PendingUploadCount = await _recordResultRepository.CountPendingUploadAsync(cancellationToken),
|
PendingUploadCount = pendingUploadCount,
|
||||||
QueuedDataBytes = await _recordResultRepository.SumPendingUploadBytesAsync(cancellationToken),
|
QueuedDataBytes = queuedDataBytes,
|
||||||
RecentSessions = recentSessionsTask.Result
|
RecentSessions = recentSessions
|
||||||
.Select(MapRecentSession)
|
.Select(MapRecentSession)
|
||||||
.ToList(),
|
.ToList(),
|
||||||
TopRooms = ComputeTopRooms(todaySessionsTask.Result)
|
TopRooms = ComputeTopRooms(todaySessions)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user