fix temp collection config and migrate it

This commit is contained in:
2026-01-19 23:04:54 +09:00
parent 828be6eb5b
commit f27db300ec
7 changed files with 400 additions and 69 deletions

View File

@@ -10,15 +10,18 @@ namespace LightlessSync.Services;
public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriberBase
{
private readonly IpcManager _ipc;
private readonly LightlessConfigService _config;
private readonly TempCollectionConfigService _config;
private readonly CancellationTokenSource _cleanupCts = new();
private int _ran;
private const int CleanupBatchSize = 50;
private static readonly TimeSpan CleanupBatchDelay = TimeSpan.FromMilliseconds(50);
private static readonly TimeSpan OrphanCleanupDelay = TimeSpan.FromDays(1);
public PenumbraTempCollectionJanitor(
ILogger<PenumbraTempCollectionJanitor> logger,
LightlessMediator mediator,
IpcManager ipc,
LightlessConfigService config) : base(logger, mediator)
TempCollectionConfigService config) : base(logger, mediator)
{
_ipc = ipc;
_config = config;
@@ -31,10 +34,6 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
if (id == Guid.Empty) return;
var changed = false;
var config = _config.Current;
if (config.OrphanableTempCollections.Add(id))
{
changed = true;
}
var now = DateTime.UtcNow;
var existing = config.OrphanableTempCollectionEntries.FirstOrDefault(entry => entry.Id == id);
@@ -63,8 +62,7 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
{
if (id == Guid.Empty) return;
var config = _config.Current;
var changed = config.OrphanableTempCollections.Remove(id);
changed |= RemoveEntry(config.OrphanableTempCollectionEntries, id) > 0;
var changed = RemoveEntry(config.OrphanableTempCollectionEntries, id) > 0;
if (changed)
{
_config.Save();
@@ -79,14 +77,31 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
if (!_ipc.Penumbra.APIAvailable)
return;
_ = Task.Run(async () =>
{
try
{
await CleanupOrphansOnBootAsync(_cleanupCts.Token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
}
catch (Exception ex)
{
Logger.LogError(ex, "Error cleaning orphaned temp collections");
}
});
}
private async Task CleanupOrphansOnBootAsync(CancellationToken token)
{
var config = _config.Current;
var ids = config.OrphanableTempCollections;
var entries = config.OrphanableTempCollectionEntries;
if (ids.Count == 0 && entries.Count == 0)
if (entries.Count == 0)
return;
var now = DateTime.UtcNow;
var changed = EnsureEntries(ids, entries, now);
var changed = EnsureEntryTimes(entries, now);
var cutoff = now - OrphanCleanupDelay;
var expired = entries
.Where(entry => entry.Id != Guid.Empty && entry.RegisteredAtUtc != DateTime.MinValue && entry.RegisteredAtUtc <= cutoff)
@@ -105,25 +120,47 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
var appId = Guid.NewGuid();
Logger.LogInformation("Cleaning up {count} orphaned Lightless temp collections older than {delay}", expired.Count, OrphanCleanupDelay);
List<Guid> removedIds = [];
foreach (var id in expired)
{
if (token.IsCancellationRequested)
{
break;
}
try
{
_ipc.Penumbra.RemoveTemporaryCollectionAsync(Logger, appId, id)
.GetAwaiter().GetResult();
await _ipc.Penumbra.RemoveTemporaryCollectionAsync(Logger, appId, id).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.LogDebug(ex, "Failed removing orphaned temp collection {id}", id);
}
removedIds.Add(id);
if (removedIds.Count % CleanupBatchSize == 0)
{
try
{
await Task.Delay(CleanupBatchDelay, token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}
}
}
foreach (var id in expired)
if (removedIds.Count == 0)
{
ids.Remove(id);
if (changed)
{
_config.Save();
}
return;
}
foreach (var id in expired)
foreach (var id in removedIds)
{
RemoveEntry(entries, id);
}
@@ -131,6 +168,17 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
_config.Save();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
_cleanupCts.Cancel();
_cleanupCts.Dispose();
}
base.Dispose(disposing);
}
private static int RemoveEntry(List<OrphanableTempCollectionEntry> entries, Guid id)
{
var removed = 0;
@@ -148,29 +196,9 @@ public sealed class PenumbraTempCollectionJanitor : DisposableMediatorSubscriber
return removed;
}
private static bool EnsureEntries(HashSet<Guid> ids, List<OrphanableTempCollectionEntry> entries, DateTime now)
private static bool EnsureEntryTimes(List<OrphanableTempCollectionEntry> entries, DateTime now)
{
var changed = false;
foreach (var id in ids)
{
if (id == Guid.Empty)
{
continue;
}
if (entries.Any(entry => entry.Id == id))
{
continue;
}
entries.Add(new OrphanableTempCollectionEntry
{
Id = id,
RegisteredAtUtc = now
});
changed = true;
}
foreach (var entry in entries)
{
if (entry.Id == Guid.Empty || entry.RegisteredAtUtc != DateTime.MinValue)