Skip to content

Commit fbc7a16

Browse files
JusterZhuclaude
andcommitted
fix(core): skip redundant chain packages after full fallback
When a chain package fails and falls back to a full replacement package, the application state is already at or beyond the full package's version. Subsequent chain packages with version ≤ the fallback version are now skipped, avoiding redundant work. Changes: - Add FallbackFullVersion to VersionEntry, DownloadAsset for version tracking - Populate FallbackFullVersion in DownloadPlanBuilder from matching full package - Track fallbackEffectiveVersion in AbstractStrategy.ExecuteAsync loop - Skip chain packages whose version ≤ fallbackEffectiveVersion - Enable StopOnFirstError in ClientTest and UpgradeTest for fallback testing - Use PackageType enum instead of magic number in ClientTest display Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 0757deb commit fbc7a16

7 files changed

Lines changed: 46 additions & 5 deletions

File tree

src/c#/GeneralUpdate.Core/Configuration/VersionEntry.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,4 +205,12 @@ public class VersionEntry : VersionIdentity
205205
/// </summary>
206206
[JsonPropertyName("fallbackFullHash")]
207207
public string? FallbackFullHash { get; set; }
208+
209+
/// <summary>
210+
/// The version string of the fallback full replacement package.
211+
/// Used by <see cref="Strategy.AbstractStrategy"/> to skip chain packages
212+
/// already covered by a previous fallback.
213+
/// </summary>
214+
[JsonPropertyName("fallbackFullVersion")]
215+
public string? FallbackFullVersion { get; set; }
208216
}

src/c#/GeneralUpdate.Core/Download/DownloadPlanBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,8 @@ public static DownloadPlan Build(
205205
{
206206
FallbackFullName = match.Name,
207207
FallbackFullUrl = match.Url,
208-
FallbackFullHash = match.SHA256
208+
FallbackFullHash = match.SHA256,
209+
FallbackFullVersion = match.Version
209210
};
210211
}
211212
return chain;

src/c#/GeneralUpdate.Core/Download/Models/DownloadAsset.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public record DownloadAsset(
1515
string? FallbackFullName = null,
1616
string? FallbackFullUrl = null,
1717
string? FallbackFullHash = null,
18+
string? FallbackFullVersion = null,
1819
bool IsForcibly = false,
1920
bool IsFreeze = false,
2021
int RecordId = 0,

src/c#/GeneralUpdate.Core/Strategy/AbstractStrategy.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using GeneralUpdate.Core.Event;
88
using GeneralUpdate.Core.Pipeline;
99
using GeneralUpdate.Core.Configuration;
10+
using GeneralUpdate.Core.Utilities;
1011

1112
using GeneralUpdate.Core.Hooks;
1213
using IUpdateReporter = GeneralUpdate.Core.Download.Reporting.IUpdateReporter;
@@ -146,8 +147,25 @@ public virtual async Task ExecuteAsync()
146147
AllPackagesSucceeded = true;
147148
var status = ReportType.None;
148149
patchRoot = StorageManager.GetTempDirectory(Patchs);
150+
151+
// Track the highest version already applied via full-package fallback,
152+
// so subsequent chain packages covered by that version are skipped.
153+
SemVersion? fallbackEffectiveVersion = null;
154+
149155
foreach (var version in _configinfo.UpdateVersions)
150156
{
157+
// When a previous chain package fell back to a full package,
158+
// the application is already at or beyond that full version.
159+
// Skip any remaining chain packages whose version is ≤ the
160+
// fallback version — applying them would be redundant.
161+
if (fallbackEffectiveVersion != null
162+
&& version.PackageType == (int)PackageType.Chain
163+
&& Semver.TryParse(version.Version, out var versionSv)
164+
&& versionSv <= fallbackEffectiveVersion)
165+
{
166+
GeneralTracer.Info($"AbstractStrategy.ExecuteAsync: skipping {version.Version} ({version.Name}) — already covered by fallback full package v{fallbackEffectiveVersion}.");
167+
continue;
168+
}
151169
try
152170
{
153171
// Use a version-specific subdirectory under patchRoot so that
@@ -193,6 +211,14 @@ public virtual async Task ExecuteAsync()
193211
var fallbackBuilder = BuildPipeline(fallbackContext);
194212
await fallbackBuilder.Build();
195213
status = ReportType.Success;
214+
// Record the fallback full package version so subsequent
215+
// chain packages ≤ this version are skipped.
216+
if (!string.IsNullOrEmpty(version.FallbackFullVersion)
217+
&& Semver.TryParse(version.FallbackFullVersion, out var ffv)
218+
&& (fallbackEffectiveVersion == null || ffv > fallbackEffectiveVersion))
219+
{
220+
fallbackEffectiveVersion = ffv;
221+
}
196222
}
197223
catch (Exception e)
198224
{

src/c#/GeneralUpdate.Core/Strategy/ClientStrategy.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,8 @@ async Task DownloadAndApplyAsync(Download.Models.DownloadPlan plan, UpdateScenar
631631
PackageType = a.PackageType,
632632
FallbackFullName = a.FallbackFullName,
633633
FallbackFullUrl = a.FallbackFullUrl,
634-
FallbackFullHash = a.FallbackFullHash
634+
FallbackFullHash = a.FallbackFullHash,
635+
FallbackFullVersion = a.FallbackFullVersion
635636
}).ToList();
636637

637638
var uVersions = dVersions.Where(v => v.AppType == (int)AppType.Upgrade).ToList();

tests/ClientTest/Program.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using GeneralUpdate.Core.Event;
88
using GeneralUpdate.Core.Hooks;
99
using GeneralUpdate.Core.Security;
10+
using GeneralUpdate.Core.Pipeline;
11+
using GeneralUpdate.Core.Configuration;
1012

1113
try
1214
{
@@ -87,6 +89,7 @@ static async Task RunUpdateTestAsync()
8789
}
8890
})
8991
.SetOption(Option.AppType, AppType.Client)
92+
.UseDiffPipeline(builder => builder.WithStopOnFirstError(true))
9093
.Hooks<ClientTestHooks>()
9194
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
9295
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)
@@ -140,7 +143,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
140143
{
141144
foreach (var vi in e.Info.Body)
142145
{
143-
var mode = vi.IsCrossVersion == true ? "CVP" : "Chain";
146+
var mode = vi.PackageType == (int)PackageType.Full ? "Full" : "Chain";
144147
var appType = vi.AppType switch
145148
{
146149
1 => "Client",
@@ -149,8 +152,7 @@ static void OnUpdateInfo(object sender, UpdateInfoEventArgs e)
149152
};
150153
Console.WriteLine($" - [{mode}] {vi.Version} ({vi.Name}) [{vi.Size} bytes] " +
151154
$"AppType={appType} " +
152-
$"{(vi.IsForcibly == true ? "(forced)" : "")}" +
153-
$"{(!string.IsNullOrEmpty(vi.FromVersion) ? $" from={vi.FromVersion}" : "")}");
155+
$"{(vi.IsForcibly == true ? "(forced)" : "")}");
154156
}
155157
}
156158
else

tests/UpgradeTest/Program.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using GeneralUpdate.Core.Download;
44
using GeneralUpdate.Core.Event;
55
using GeneralUpdate.Core.Hooks;
6+
using GeneralUpdate.Core.Pipeline;
67

78
try
89
{
@@ -41,6 +42,7 @@ static async Task RunStandardUpgradeAsync()
4142

4243
await new GeneralUpdateBootstrap()
4344
.SetOption(Option.AppType, AppType.Upgrade)
45+
.UseDiffPipeline(builder => builder.WithStopOnFirstError(true))
4446
.Hooks<UpgradeTestHooks>()
4547
.AddListenerMultiDownloadStatistics(OnDownloadStatistics)
4648
.AddListenerMultiDownloadCompleted(OnDownloadCompleted)

0 commit comments

Comments
 (0)