Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion backend/FwLite/FwDataMiniLcmBridge/FwDataFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public class FwDataFactory(
IOptions<FwDataBridgeConfig> config) : IDisposable, IHostedService
{
private bool _shuttingDown = false;

// Sliding window before an idle LcmCache is evicted and disposed. PreventEviction refreshes inside this window.
private static readonly TimeSpan CacheSlidingExpiration = TimeSpan.FromMinutes(30);

public FwDataFactory(ILogger<FwDataMiniLcmApi> fwdataLogger,
IMemoryCache cache,
ILogger<FwDataFactory> logger,
Expand Down Expand Up @@ -51,7 +55,7 @@ private LcmCache GetProjectServiceCached(FwDataProject project)
var projectService = cache.GetOrCreate(key,
entry =>
{
entry.SlidingExpiration = TimeSpan.FromMinutes(30);
entry.SlidingExpiration = CacheSlidingExpiration;
entry.RegisterPostEvictionCallback(OnLcmProjectCacheEviction, (logger, _projectCacheKeys));
logger.LogInformation("Loading project {ProjectFileName}", project.FileName);
var projectService = projectLoader.LoadCache(project);
Expand Down Expand Up @@ -131,6 +135,24 @@ public IAsyncDisposable DeferCloseAsync(FwDataProject project)
return Defer.Async(() => CloseProjectAsync(project));
}

/// <summary>
/// Keeps the project's LcmCache from being evicted until disposed, by periodically resetting its sliding expiration.
/// Use around long-running work that holds one api instance past the expiration window (e.g. a sync paused in a debugger).
/// </summary>
public IDisposable PreventEviction(FwDataProject project)
{
var key = CacheKey(project);
// Refresh now in case little of the window remains, then every half-window so a tick can't be missed.
var period = CacheSlidingExpiration / 2;
return new Timer(_ =>
{
// Best-effort: the shared cache can be disposed during shutdown while a sync is still finishing,
// and an unhandled throw on this timer thread would take the process down.
try { cache.TryGetValue(key, out _); }
catch (ObjectDisposedException) { }
}, null, TimeSpan.Zero, period);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using FwDataMiniLcmBridge;
using FwDataMiniLcmBridge.Api;
using LcmCrdt;
using LexCore.Sync;
Expand All @@ -12,7 +13,8 @@ namespace FwLiteProjectSync;
public class CrdtFwdataProjectSyncService(MiniLcmImport miniLcmImport,
ILogger<CrdtFwdataProjectSyncService> logger,
MiniLcmApiValidationWrapperFactory validationWrapperFactory,
CrdtProjectsService crdtProjectsService)
CrdtProjectsService crdtProjectsService,
FwDataFactory fwDataFactory)
{
public record DryRunSyncResult(
int CrdtChanges,
Expand Down Expand Up @@ -53,6 +55,9 @@ private async Task<SyncResult> SyncOrImportInternal(IMiniLcmApi crdtApi, IMiniLc
throw new InvalidOperationException($"Project id mismatch, CRDT Id: {crdt.ProjectData.FwProjectId}, FWData Id: {fwdata.ProjectId}");
}

// A sync that outlives the LcmCache's sliding expiration (e.g. paused in a debugger) would otherwise have it disposed mid-sync.
using var keepFwdataAlive = fwDataFactory.PreventEviction(fwdata.Project);

// Project snapshot logic/handling is done outside of this class so that Sync vs Import is explicit.
// We still choose to explicitly verify a consistent state to avoid accidental misuse.
var hasSyncedSuccessfully = ProjectSnapshotService.HasSyncedSuccessfully(fwdata.Project);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ private ServiceProvider BuildServiceProvider(
null!,
NullLogger<CrdtFwdataProjectSyncService>.Instance,
null!,
null!,
null!);

syncService
Expand Down
Loading