mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-20 23:54:26 +08:00
Introduce render gate and chart caching
Replace UI DispatcherTimer polling with a StudySnapshotRenderGate across multiple widgets to queue and apply only the latest analytics snapshot; components updated include StudyDeductionReasonsWidget, StudyEnvironmentWidget, StudyInterruptDensityWidget, StudyNoiseCurveWidget. Add StudySnapshotRenderGate implementation to coordinate rendering and monitoring leases and update subscription/lease lifecycle handling (subscribe/unsubscribe, Acquire/Dispose leases, Clear/Dispose gate). Rewrite chart controls (StudyNoiseCurveChartControl and StudyNoiseDistributionScatterChartControl) to use stable logical-time origins, split series into static vs dynamic tails, add geometry/sample caching, stable jitter/coordinate mapping helpers, and expose internal helpers & counts for testing. Add unit tests (StudyComponentRenderingTests) covering the render gate and chart behaviors (layer counts, logical X mapping, stable jitter, cache rebuild). These changes improve rendering correctness and performance by avoiding redundant renders and enabling deterministic chart layout.
This commit is contained in:
@@ -251,7 +251,7 @@ internal sealed class UpdateEngineService
|
||||
snapshot.Status = "applied";
|
||||
SaveSnapshot(snapshotPath, snapshot);
|
||||
CleanupIncomingArtifacts();
|
||||
CleanupDestroyedDeployments();
|
||||
RetainDeploymentsForRollback();
|
||||
|
||||
_progressReporter.ReportProgress(new ContractsUpdate.InstallProgressReport(ContractsUpdate.InstallStage.Completed, $"Updated to {targetVersion}.", 100, null, fileMap.Files.Count, fileMap.Files.Count));
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(true, currentVersion, targetVersion, null, false));
|
||||
@@ -269,19 +269,24 @@ internal sealed class UpdateEngineService
|
||||
catch (Exception ex)
|
||||
{
|
||||
_progressReporter.ReportProgress(new ContractsUpdate.InstallProgressReport(ContractsUpdate.InstallStage.RollingBack, "Rolling back...", 0, null, 0, 0));
|
||||
TryRollbackOnFailure(snapshot);
|
||||
snapshot.Status = "rolled_back";
|
||||
var rollbackResult = TryRollbackOnFailure(snapshot);
|
||||
snapshot.Status = rollbackResult.Success ? "rolled_back" : "rollback_failed";
|
||||
SaveSnapshot(snapshotPath, snapshot);
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(false, currentVersion, targetVersion, ex.Message, true));
|
||||
var errorMessage = rollbackResult.Success
|
||||
? ex.Message
|
||||
: $"{ex.Message}; rollback failed: {rollbackResult.ErrorMessage}";
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(false, currentVersion, targetVersion, errorMessage, rollbackResult.Success));
|
||||
return new LauncherResult
|
||||
{
|
||||
Success = false,
|
||||
Stage = "update.apply",
|
||||
Code = "apply_failed",
|
||||
Message = "Failed to apply update. Rolled back to previous version.",
|
||||
ErrorMessage = ex.Message,
|
||||
Code = rollbackResult.Success ? "apply_failed" : "rollback_failed",
|
||||
Message = rollbackResult.Success
|
||||
? "Failed to apply update. Rolled back to previous version."
|
||||
: "Failed to apply update and rollback failed.",
|
||||
ErrorMessage = errorMessage,
|
||||
CurrentVersion = currentVersion,
|
||||
RolledBackTo = currentVersion
|
||||
RolledBackTo = rollbackResult.Success ? currentVersion : null
|
||||
};
|
||||
}
|
||||
finally
|
||||
@@ -410,7 +415,7 @@ internal sealed class UpdateEngineService
|
||||
snapshot.Status = "applied";
|
||||
SaveSnapshot(snapshotPath, snapshot);
|
||||
CleanupIncomingArtifacts();
|
||||
CleanupDestroyedDeployments();
|
||||
RetainDeploymentsForRollback();
|
||||
|
||||
_progressReporter.ReportProgress(new ContractsUpdate.InstallProgressReport(ContractsUpdate.InstallStage.Completed, $"Updated to {targetVersion}.", 100, null, fileEntries.Count, fileEntries.Count));
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(true, sourceVersion, targetVersion, null, false));
|
||||
@@ -456,19 +461,24 @@ internal sealed class UpdateEngineService
|
||||
}
|
||||
|
||||
_progressReporter.ReportProgress(new ContractsUpdate.InstallProgressReport(ContractsUpdate.InstallStage.RollingBack, "Rolling back...", 0, null, 0, 0));
|
||||
TryRollbackOnFailure(snapshot);
|
||||
snapshot.Status = "rolled_back";
|
||||
var rollbackResult = TryRollbackOnFailure(snapshot);
|
||||
snapshot.Status = rollbackResult.Success ? "rolled_back" : "rollback_failed";
|
||||
SaveSnapshot(snapshotPath, snapshot);
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(false, sourceVersion, targetVersion, ex.Message, true));
|
||||
var errorMessage = rollbackResult.Success
|
||||
? ex.Message
|
||||
: $"{ex.Message}; rollback failed: {rollbackResult.ErrorMessage}";
|
||||
_progressReporter.ReportComplete(new ContractsUpdate.InstallCompleteReport(false, sourceVersion, targetVersion, errorMessage, rollbackResult.Success));
|
||||
return new LauncherResult
|
||||
{
|
||||
Success = false,
|
||||
Stage = "update.apply",
|
||||
Code = "apply_failed",
|
||||
Message = "Failed to apply PLONDS update. Rolled back to previous version.",
|
||||
ErrorMessage = ex.Message,
|
||||
Code = rollbackResult.Success ? "apply_failed" : "rollback_failed",
|
||||
Message = rollbackResult.Success
|
||||
? "Failed to apply PLONDS update. Rolled back to previous version."
|
||||
: "Failed to apply PLONDS update and rollback failed.",
|
||||
ErrorMessage = errorMessage,
|
||||
CurrentVersion = sourceVersion,
|
||||
RolledBackTo = sourceVersion
|
||||
RolledBackTo = rollbackResult.Success ? sourceVersion : null
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1375,6 +1385,11 @@ internal sealed class UpdateEngineService
|
||||
return Failed("update.rollback", "invalid_snapshot", "Invalid snapshot metadata.");
|
||||
}
|
||||
|
||||
if (!Directory.Exists(snapshot.SourceDirectory))
|
||||
{
|
||||
return Failed("update.rollback", "source_missing", $"Rollback source deployment is missing: {snapshot.SourceDirectory}");
|
||||
}
|
||||
|
||||
var currentDeployment = _deploymentLocator.FindCurrentDeploymentDirectory();
|
||||
if (string.IsNullOrWhiteSpace(currentDeployment))
|
||||
{
|
||||
@@ -1397,21 +1412,7 @@ internal sealed class UpdateEngineService
|
||||
|
||||
public void CleanupDestroyedDeployments()
|
||||
{
|
||||
foreach (var dir in Directory.EnumerateDirectories(_appRoot, "app-*", SearchOption.TopDirectoryOnly))
|
||||
{
|
||||
if (!File.Exists(Path.Combine(dir, ".destroy")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Directory.Delete(dir, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
RetainDeploymentsForRollback();
|
||||
}
|
||||
|
||||
private void ApplyFileEntry(UpdateFileEntry file, string currentDeployment, string targetDeployment, string extractRoot)
|
||||
@@ -1459,9 +1460,15 @@ internal sealed class UpdateEngineService
|
||||
var toCurrent = Path.Combine(toDeployment, ".current");
|
||||
var fromCurrent = Path.Combine(fromDeployment, ".current");
|
||||
var fromDestroy = Path.Combine(fromDeployment, ".destroy");
|
||||
var toDestroy = Path.Combine(toDeployment, ".destroy");
|
||||
var toPartial = Path.Combine(toDeployment, ".partial");
|
||||
|
||||
File.WriteAllText(toCurrent, string.Empty);
|
||||
if (File.Exists(toDestroy))
|
||||
{
|
||||
File.Delete(toDestroy);
|
||||
}
|
||||
|
||||
if (File.Exists(fromCurrent))
|
||||
{
|
||||
File.Delete(fromCurrent);
|
||||
@@ -1474,7 +1481,7 @@ internal sealed class UpdateEngineService
|
||||
}
|
||||
}
|
||||
|
||||
private void TryRollbackOnFailure(SnapshotMetadata snapshot)
|
||||
private RollbackAttemptResult TryRollbackOnFailure(SnapshotMetadata snapshot)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -1483,6 +1490,11 @@ internal sealed class UpdateEngineService
|
||||
Directory.Delete(snapshot.TargetDirectory, true);
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(snapshot.SourceDirectory) || !Directory.Exists(snapshot.SourceDirectory))
|
||||
{
|
||||
return new RollbackAttemptResult(false, "Source deployment is missing.");
|
||||
}
|
||||
|
||||
if (File.Exists(Path.Combine(snapshot.SourceDirectory, ".destroy")))
|
||||
{
|
||||
File.Delete(Path.Combine(snapshot.SourceDirectory, ".destroy"));
|
||||
@@ -1492,12 +1504,22 @@ internal sealed class UpdateEngineService
|
||||
{
|
||||
File.WriteAllText(Path.Combine(snapshot.SourceDirectory, ".current"), string.Empty);
|
||||
}
|
||||
|
||||
return new RollbackAttemptResult(true, null);
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new RollbackAttemptResult(false, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void RetainDeploymentsForRollback()
|
||||
{
|
||||
_deploymentLocator.CleanupOldDeployments(minVersionsToKeep: 3);
|
||||
}
|
||||
|
||||
private sealed record RollbackAttemptResult(bool Success, string? ErrorMessage);
|
||||
|
||||
internal void CleanupIncomingArtifacts()
|
||||
{
|
||||
foreach (var path in new[]
|
||||
|
||||
Reference in New Issue
Block a user