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
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,42 @@ private async Task<IBrowser> GetBrowserAsync()

var funcBrowser = new Func<Task<IBrowser>>(GetBrowserAsync);

// Issue #613: collect per-set failures so one set timing out (e.g. a large set under high
// parallelism) does not abort the whole multi-language harvest. All reachable sets are
// attempted and persisted; a single aggregate error is raised at the end if any failed.
var failedSets = new ConcurrentBag<(string cardSet, string language, string error)>();

var parallelOptionsCardset = new ParallelOptions { MaxDegreeOfParallelism = Config.EnableParallelism? Config.MaxDegreeOfParallelismCardpen : 1 };
await Parallel.ForEachAsync(targetCardSets, parallelOptionsCardset, async (configCardSet, token) =>
{
var targetLanguages = AssetConverterConfig.LocalizationConfig.BuildLanguageList(configCardSet.Translations);
var parallelOptionsCardsetLanguage = new ParallelOptions { MaxDegreeOfParallelism = Config.EnableParallelism? Config.MaxDegreeOfParallelismCardpenTranslations : 1 };
await Parallel.ForEachAsync(targetLanguages, parallelOptionsCardsetLanguage, async (currentLanguage, newToken) =>
{
await ProcessLocalizedHarvest(configCardSet, currentLanguage, harvestDictionary, funcBrowser);
try
{
await ProcessLocalizedHarvest(configCardSet, currentLanguage, harvestDictionary, funcBrowser);
}
catch (Exception ex) when (Config.ContinueOnHarvestSetFailure)
{
// Persist the failure and keep harvesting the other sets (issue #613).
failedSets.Add((configCardSet.Name, currentLanguage, ex.Message));
Logger.Log($"[HARVEST-FAILURE] Card set '{configCardSet.Name}' / '{currentLanguage}' failed and was skipped: {ex.Message}", MessageType.Problem);
}
});
});

if (!failedSets.IsEmpty)
{
var summary = string.Join("; ", failedSets.Select(f => $"{f.cardSet}/{f.language}"));
Logger.Log($"[HARVEST-PARTIAL] {failedSets.Count} card set(s) failed to harvest and were skipped: {summary}. " +
"All successful harvests were persisted to disk — re-run to collect the missing sets (the cache skips the good ones). " +
"If large sets time out, set EnableParallelism=false or lower MaxDegreeOfParallelismCardpen (issue #613).", MessageType.Problem);
throw new ApplicationException(
$"Harvest completed with {failedSets.Count} failed card set(s): {summary}. " +
"Successful harvests were persisted; re-run to collect the missing sets (issue #613).");
}

return harvestDictionary;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ public class WebBasedGeneratorConfig

public int MaxDegreeOfParallelismCardpenTranslations { get; set; } = 2;

/// <summary>
/// Issue #613: when true (default), a card set whose harvest fails (e.g. a large set
/// timing out under high parallelism) is logged and skipped so the remaining sets still
/// harvest and persist to disk; a single aggregate error is raised at the end of the run
/// listing every failed set. A re-run then skips the good harvests (cache) and only
/// re-collects the missing ones. When false, the first failure aborts the whole harvest
/// (legacy behavior).
/// </summary>
public bool ContinueOnHarvestSetFailure { get; set; } = true;

public int MaxDegreeOfParallelismImages { get; set; } = 3;

public int MaxDegreeOfParallelismImageTranslations { get; set; } = 2;
Expand Down
Loading