using LightlessSync.API.Dto; using LightlessSync.API.SignalR; using LightlessSyncServer.Hubs; using LightlessSyncServer.Models; using LightlessSyncShared.Data; using LightlessSyncShared.Metrics; using LightlessSyncShared.Services; using LightlessSyncShared.Utils.Configuration; using Microsoft.AspNetCore.SignalR; using Microsoft.EntityFrameworkCore; using StackExchange.Redis.Extensions.Core.Abstractions; namespace LightlessSyncServer.Services; public sealed class SystemInfoService : BackgroundService { private readonly LightlessMetrics _lightlessMetrics; private readonly IConfigurationService _config; private readonly IDbContextFactory _dbContextFactory; private readonly ILogger _logger; private readonly IHubContext _hubContext; private readonly IRedisDatabase _redis; public SystemInfoDto SystemInfoDto { get; private set; } = new(); public SystemInfoService(LightlessMetrics lightlessMetrics, IConfigurationService configurationService, IDbContextFactory dbContextFactory, ILogger logger, IHubContext hubContext, IRedisDatabase redisDb) { _lightlessMetrics = lightlessMetrics; _config = configurationService; _dbContextFactory = dbContextFactory; _logger = logger; _hubContext = hubContext; _redis = redisDb; } public override async Task StartAsync(CancellationToken cancellationToken) { await base.StartAsync(cancellationToken).ConfigureAwait(false); _logger.LogInformation("System Info Service started"); } protected override async Task ExecuteAsync(CancellationToken ct) { var timeOut = _config.IsMain ? 15 : 30; while (!ct.IsCancellationRequested) { try { ThreadPool.GetAvailableThreads(out int workerThreads, out int ioThreads); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableWorkerThreads, workerThreads); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeAvailableIOWorkerThreads, ioThreads); var onlineUsers = (_redis.SearchKeysAsync("UID:*").GetAwaiter().GetResult()).Count(); var allLightfinderKeys = _redis.SearchKeysAsync("broadcast:*").GetAwaiter().GetResult().Where(c => !c.Contains("owner", StringComparison.Ordinal)).ToHashSet(StringComparer.Ordinal); var allLightfinderItems = _redis.GetAllAsync(allLightfinderKeys).GetAwaiter().GetResult(); var countLightFinderUsers = allLightfinderItems.Count; var countLightFinderSyncshells = allLightfinderItems.Count(static l => !string.IsNullOrEmpty(l.Value.GID)); SystemInfoDto = new SystemInfoDto() { OnlineUsers = onlineUsers, }; if (_config.IsMain) { _logger.LogInformation("Sending System Info, Online Users: {onlineUsers}", onlineUsers); await _hubContext.Clients.All.Client_UpdateSystemInfo(SystemInfoDto).ConfigureAwait(false); using var db = await _dbContextFactory.CreateDbContextAsync(ct).ConfigureAwait(false); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, onlineUsers); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeLightFinderConnections, countLightFinderUsers); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugePairs, db.ClientPairs.AsNoTracking().Count()); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugePairsPaused, db.Permissions.AsNoTracking().Count(p => p.IsPaused)); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeGroups, db.Groups.AsNoTracking().Count()); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeGroupPairs, db.GroupPairs.AsNoTracking().Count()); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeLightFinderGroups, countLightFinderSyncshells); _lightlessMetrics.SetGaugeTo(MetricsAPI.GaugeUsersRegistered, db.Users.AsNoTracking().Count()); } await Task.Delay(TimeSpan.FromSeconds(timeOut), ct).ConfigureAwait(false); } catch (Exception ex) { _logger.LogWarning(ex, "Failed to push system info"); } } } }