mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
5.8 KiB
5.8 KiB
Memory Optimization Recommendations for Allstarr
Current Implementation Status
✅ COMPLETED: Mark-for-deletion system with 24-hour delay ✅ COMPLETED: Persistent favorites tracking using JSON files ✅ COMPLETED: Cache-first copying for favorites (avoids re-downloads) ✅ COMPLETED: Dependency injection for CacheCleanupService to process pending deletions
Memory Optimization Strategies
1. Collection Optimizations
Current Issues:
- Multiple
List<Song>,List<Album>,List<Artist>collections created during searches - Large
Dictionary<string, object?>objects for Jellyfin metadata - Concurrent collections like
ConcurrentDictionary<string, SessionInfo>for sessions
Recommendations:
// Use ArrayPool for temporary collections
private static readonly ArrayPool<Song> SongArrayPool = ArrayPool<Song>.Shared;
// Use Span<T> for temporary operations
ReadOnlySpan<Song> ProcessSongs(ReadOnlySpan<Song> songs) { ... }
// Use IAsyncEnumerable for streaming large results
IAsyncEnumerable<Song> SearchSongsStreamAsync(string query);
2. JSON Serialization Optimizations
Current Issues:
- Heavy use of
JsonSerializer.Deserialize<Dictionary<string, object?>>() - Multiple serialization/deserialization cycles for caching
Recommendations:
// Use System.Text.Json source generators for better performance
[JsonSerializable(typeof(List<Song>))]
[JsonSerializable(typeof(Dictionary<string, FavoriteTrackInfo>))]
public partial class AllstarrJsonContext : JsonSerializerContext { }
// Use JsonDocument for read-only scenarios instead of Dictionary
JsonDocument.Parse(json).RootElement.GetProperty("Items")
3. Caching Strategy Improvements
Current Issues:
- File-based caching creates multiple copies of data
- Redis and file caches can contain duplicate data
Recommendations:
// Implement cache eviction policies
public class LRUCache<TKey, TValue> where TKey : notnull
{
private readonly int _maxSize;
private readonly Dictionary<TKey, LinkedListNode<CacheItem>> _cache;
private readonly LinkedList<CacheItem> _lruList;
}
// Use weak references for large objects
private readonly WeakReference<List<Song>> _cachedSongs = new(null);
4. String Interning and Optimization
Current Issues:
- Many duplicate strings (artist names, album titles) across collections
- Path strings created repeatedly
Recommendations:
// Use string interning for common values
private static readonly ConcurrentDictionary<string, string> InternedStrings = new();
public static string Intern(string value) => InternedStrings.GetOrAdd(value, v => v);
// Use StringBuilder for path construction
private static readonly ThreadLocal<StringBuilder> PathBuilder =
new(() => new StringBuilder(256));
5. Background Service Optimizations
Current Issues:
- Multiple background services running simultaneously
- Potential memory leaks in long-running services
Recommendations:
// Implement proper disposal patterns
public class CacheCleanupService : BackgroundService, IDisposable
{
private readonly SemaphoreSlim _semaphore = new(1, 1);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var _ = await _semaphore.WaitAsync(stoppingToken);
// ... cleanup logic
}
public override void Dispose()
{
_semaphore?.Dispose();
base.Dispose();
}
}
6. HTTP Client Optimizations
Current Issues:
- Multiple HTTP clients for different services
- Large response buffers
Recommendations:
// Use HttpClientFactory with proper configuration
builder.Services.AddHttpClient<DeezerMetadataService>(client =>
{
client.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.0");
client.Timeout = TimeSpan.FromSeconds(30);
})
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
{
MaxConnectionsPerServer = 10,
PooledConnectionLifetime = TimeSpan.FromMinutes(15)
});
// Stream large responses instead of loading into memory
using var stream = await response.Content.ReadAsStreamAsync();
using var reader = new StreamReader(stream);
7. Container Memory Limits
Docker Configuration:
# Set memory limits in docker-compose.yml
services:
allstarr:
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 256M
Runtime Configuration:
// Configure GC for container environments
GCSettings.LatencyMode = GCLatencyMode.Batch;
GC.Collect(2, GCCollectionMode.Optimized);
Immediate Actions (Priority Order)
- Enable GC monitoring - Add memory usage logging to identify hotspots
- Implement cache size limits - Prevent unbounded growth of in-memory caches
- Use object pooling - For frequently allocated objects like Song/Album/Artist
- Stream large responses - Instead of loading entire JSON responses into memory
- Optimize JSON serialization - Use source generators and reduce Dictionary usage
Monitoring Recommendations
// Add memory monitoring to Program.cs
builder.Services.AddHostedService<MemoryMonitoringService>();
public class MemoryMonitoringService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var memoryUsage = GC.GetTotalMemory(false);
var gen0 = GC.CollectionCount(0);
var gen1 = GC.CollectionCount(1);
var gen2 = GC.CollectionCount(2);
_logger.LogInformation("Memory: {Memory:N0} bytes, GC: Gen0={Gen0}, Gen1={Gen1}, Gen2={Gen2}",
memoryUsage, gen0, gen1, gen2);
await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
}
}
}