Handle shutdowns better, allow retrying multis
This commit is contained in:
@@ -42,6 +42,10 @@
|
|||||||
[ForbiddenSetting([Apr2025], EntranceShuffle.Swapped)]
|
[ForbiddenSetting([Apr2025], EntranceShuffle.Swapped)]
|
||||||
public EntranceShuffle EntranceShuffle { get; set; } = EntranceShuffle.Vanilla;
|
public EntranceShuffle EntranceShuffle { get; set; } = EntranceShuffle.Vanilla;
|
||||||
|
|
||||||
|
[NoSettingName]
|
||||||
|
[RequiredSetting([Apr2025], LinksHouse.Vanilla)]
|
||||||
|
public LinksHouse LinksHouse { get; set; } = LinksHouse.Vanilla;
|
||||||
|
|
||||||
[SettingName("skullwoods")]
|
[SettingName("skullwoods")]
|
||||||
[RequiredSetting([Apr2025], SkullWoodsShuffle.Original)]
|
[RequiredSetting([Apr2025], SkullWoodsShuffle.Original)]
|
||||||
[NoSettingName([Apr2025])]
|
[NoSettingName([Apr2025])]
|
||||||
@@ -250,6 +254,11 @@
|
|||||||
[SettingName("insanity")] Decoupled,
|
[SettingName("insanity")] Decoupled,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum LinksHouse {
|
||||||
|
Vanilla,
|
||||||
|
[AdditionalSetting("--shufflelinks")] Shuffled,
|
||||||
|
}
|
||||||
|
|
||||||
public enum SkullWoodsShuffle {
|
public enum SkullWoodsShuffle {
|
||||||
Original,
|
Original,
|
||||||
Restricted,
|
Restricted,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@
|
|||||||
builder.Services.AddSingleton(sp => sp);
|
builder.Services.AddSingleton(sp => sp);
|
||||||
builder.Services.AddSingleton<AzureStorage>();
|
builder.Services.AddSingleton<AzureStorage>();
|
||||||
builder.Services.AddSingleton<CommonSettingsProcessor>();
|
builder.Services.AddSingleton<CommonSettingsProcessor>();
|
||||||
|
builder.Services.AddSingleton<ShutdownHandler>();
|
||||||
|
|
||||||
builder.Services.AddKeyedScoped<IRandomizer, BaseRandomizer>(BaseRandomizer.Name);
|
builder.Services.AddKeyedScoped<IRandomizer, BaseRandomizer>(BaseRandomizer.Name);
|
||||||
builder.Services.AddKeyedScoped<IRandomizer, BaseRandomizer>(BaseRandomizer.DungeonMapName);
|
builder.Services.AddKeyedScoped<IRandomizer, BaseRandomizer>(BaseRandomizer.DungeonMapName);
|
||||||
@@ -90,7 +91,11 @@
|
|||||||
c.EnableValidator();
|
c.EnableValidator();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var sh = app.Services.GetService<ShutdownHandler>();
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
sh?.HandleShutdown().Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,18 +18,20 @@
|
|||||||
public const string Name = "base";
|
public const string Name = "base";
|
||||||
public const string DungeonMapName = "dungeon_map";
|
public const string DungeonMapName = "dungeon_map";
|
||||||
|
|
||||||
public const int MULTI_TRIES = 20;
|
public const int MULTI_TRIES = 80;
|
||||||
public const int SINGLE_TRIES = 20;
|
public const int SINGLE_TRIES = 5;
|
||||||
|
|
||||||
public BaseRandomizer(
|
public BaseRandomizer(
|
||||||
AzureStorage azureStorage,
|
AzureStorage azureStorage,
|
||||||
CommonSettingsProcessor settingsProcessor,
|
CommonSettingsProcessor settingsProcessor,
|
||||||
IdGenerator idGenerator,
|
IdGenerator idGenerator,
|
||||||
|
ShutdownHandler shutdownHandler,
|
||||||
IOptionsMonitor<ServiceOptions> optionsMonitor,
|
IOptionsMonitor<ServiceOptions> optionsMonitor,
|
||||||
ILogger<BaseRandomizer> logger) {
|
ILogger<BaseRandomizer> logger) {
|
||||||
this.AzureStorage = azureStorage;
|
this.AzureStorage = azureStorage;
|
||||||
this.SettingsProcessor = settingsProcessor;
|
this.SettingsProcessor = settingsProcessor;
|
||||||
this.IdGenerator = idGenerator;
|
this.IdGenerator = idGenerator;
|
||||||
|
this.ShutdownHandler = shutdownHandler;
|
||||||
this.OptionsMonitor = optionsMonitor;
|
this.OptionsMonitor = optionsMonitor;
|
||||||
this.Logger = logger;
|
this.Logger = logger;
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,7 @@
|
|||||||
private IOptionsMonitor<ServiceOptions> OptionsMonitor { get; }
|
private IOptionsMonitor<ServiceOptions> OptionsMonitor { get; }
|
||||||
private ILogger<BaseRandomizer> Logger { get; }
|
private ILogger<BaseRandomizer> Logger { get; }
|
||||||
private ServiceOptions Configuration => OptionsMonitor.CurrentValue;
|
private ServiceOptions Configuration => OptionsMonitor.CurrentValue;
|
||||||
|
private ShutdownHandler ShutdownHandler { get; }
|
||||||
|
|
||||||
public void Validate(SeedSettings settings) {
|
public void Validate(SeedSettings settings) {
|
||||||
this.SettingsProcessor.ValidateSettings(settings.Randomizer, settings);
|
this.SettingsProcessor.ValidateSettings(settings.Randomizer, settings);
|
||||||
@@ -58,7 +61,6 @@
|
|||||||
var args = new List<string>() {
|
var args = new List<string>() {
|
||||||
"--reduce_flashing",
|
"--reduce_flashing",
|
||||||
"--quickswap",
|
"--quickswap",
|
||||||
"--shufflelinks",
|
|
||||||
"--shuffletavern",
|
"--shuffletavern",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -100,13 +102,11 @@
|
|||||||
|
|
||||||
args.Add("--spoiler=json");
|
args.Add("--spoiler=json");
|
||||||
|
|
||||||
args.Add(string.Format("--tries={0}", SINGLE_TRIES));
|
|
||||||
|
|
||||||
foreach (var arg in settings) {
|
foreach (var arg in settings) {
|
||||||
args.Add(arg);
|
args.Add(arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.LogInformation("Randomizing with args: {args}", string.Join(" ", args));
|
Logger.LogInformation("Randomizing {id} with args: {args}", id, string.Join(" ", args.Select(arg => $"\"{arg}\"")));
|
||||||
|
|
||||||
var generating = string.Format("{0}/generating", id);
|
var generating = string.Format("{0}/generating", id);
|
||||||
await AzureStorage.UploadFile(generating, BinaryData.Empty);
|
await AzureStorage.UploadFile(generating, BinaryData.Empty);
|
||||||
@@ -114,14 +114,17 @@
|
|||||||
var process = Process.Start(start) ?? throw new GenerationFailedException("Process failed to start.");
|
var process = Process.Start(start) ?? throw new GenerationFailedException("Process failed to start.");
|
||||||
process.EnableRaisingEvents = true;
|
process.EnableRaisingEvents = true;
|
||||||
|
|
||||||
process.OutputDataReceived += (_, args) => Logger.LogInformation("Randomizer STDOUT: {output}", args.Data);
|
process.OutputDataReceived += (_, args) => Logger.LogInformation("Randomizer {id} STDOUT: {output}", id, args.Data);
|
||||||
process.ErrorDataReceived += (_, args) => Logger.LogInformation("Randomizer STDERR: {output}", args.Data);
|
process.ErrorDataReceived += (_, args) => Logger.LogInformation("Randomizer {id} STDERR: {output}", id, args.Data);
|
||||||
|
|
||||||
process.BeginOutputReadLine();
|
process.BeginOutputReadLine();
|
||||||
process.BeginErrorReadLine();
|
process.BeginErrorReadLine();
|
||||||
|
|
||||||
|
this.ShutdownHandler.AddId(id);
|
||||||
|
|
||||||
process.Exited += async (sender, args) => {
|
process.Exited += async (sender, args) => {
|
||||||
try {
|
try {
|
||||||
|
this.ShutdownHandler.RemoveId(id);
|
||||||
await completed.Invoke(process.ExitCode);
|
await completed.Invoke(process.ExitCode);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
this.Logger.LogError(ex, "Error while invoking completion of randomizer generation.");
|
this.Logger.LogError(ex, "Error while invoking completion of randomizer generation.");
|
||||||
@@ -132,7 +135,9 @@
|
|||||||
public async Task Randomize(string id, SeedSettings settings) {
|
public async Task Randomize(string id, SeedSettings settings) {
|
||||||
Logger.LogDebug("Recieved request for id {id} to randomize settings {@settings}", id, settings);
|
Logger.LogDebug("Recieved request for id {id} to randomize settings {@settings}", id, settings);
|
||||||
|
|
||||||
await StartProcess(this.SettingsProcessor.GetRandomizerName(settings.Randomizer), id, this.GetArgs(settings), async exitcode => {
|
var args = this.GetArgs(settings).Append(string.Format("--tries={0}", SINGLE_TRIES));
|
||||||
|
|
||||||
|
await StartProcess(this.SettingsProcessor.GetRandomizerName(settings.Randomizer), id, args, async exitcode => {
|
||||||
if (exitcode != 0) {
|
if (exitcode != 0) {
|
||||||
await GenerationFailed(id, exitcode);
|
await GenerationFailed(id, exitcode);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -66,6 +66,12 @@
|
|||||||
return ResolveResult(await this.SeedService.GetMulti(id));
|
return ResolveResult(await this.SeedService.GetMulti(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Route("/multi/{id}")]
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<ActionResult> RetryMulti(string id) {
|
||||||
|
return ResolveResult(await this.RandomizeService.RetryMulti(id));
|
||||||
|
}
|
||||||
|
|
||||||
private ActionResult ResolveResult(IDictionary<string, object> result) {
|
private ActionResult ResolveResult(IDictionary<string, object> result) {
|
||||||
if (result.TryGetValue("status", out var responseCode)) {
|
if (result.TryGetValue("status", out var responseCode)) {
|
||||||
if (responseCode is int code) {
|
if (responseCode is int code) {
|
||||||
|
|||||||
@@ -95,5 +95,46 @@
|
|||||||
result["status"] = 202;
|
result["status"] = 202;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IDictionary<string, object>> RetryMulti(string multiId) {
|
||||||
|
var files = await this.AzureStorage.GetFiles(multiId);
|
||||||
|
|
||||||
|
this.Logger.LogDebug("Found files: {@files}", files.Keys);
|
||||||
|
|
||||||
|
var result = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
if (!files.TryGetValue("settings.json", out var settingsData)) {
|
||||||
|
result["status"] = 404;
|
||||||
|
result["error"] = "multi settings not found";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var settingsJson = JsonDocument.Parse(settingsData.ToString());
|
||||||
|
|
||||||
|
var settings = settingsJson.Deserialize<IList<SeedSettings>>(JsonOptions.Default);
|
||||||
|
|
||||||
|
if (settings == null) {
|
||||||
|
result["status"] = 404;
|
||||||
|
result["error"] = "multi settings not found";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.TryGetValue("patch.bps", out var patchData)) {
|
||||||
|
result["status"] = 409;
|
||||||
|
result["error"] = "generation already successful";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (files.ContainsKey("generating")) {
|
||||||
|
result["status"] = 409;
|
||||||
|
result["error"] = "generation still in progress";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.RandomizeMultiworld(settings, multiId);
|
||||||
|
|
||||||
|
result["status"] = 202;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,7 @@
|
|||||||
} else {
|
} else {
|
||||||
result["status"] = 404;
|
result["status"] = 404;
|
||||||
result["error"] = "generation failed";
|
result["error"] = "generation failed";
|
||||||
|
result["retry"] = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
ALttPRandomizer/ShutdownHandler.cs
Normal file
40
ALttPRandomizer/ShutdownHandler.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
namespace ALttPRandomizer {
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using ALttPRandomizer.Azure;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
public class ShutdownHandler {
|
||||||
|
public ShutdownHandler(AzureStorage azureStorage, ILogger<ShutdownHandler> logger) {
|
||||||
|
this.AzureStorage = azureStorage;
|
||||||
|
this.Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AzureStorage AzureStorage { get; }
|
||||||
|
private ILogger<ShutdownHandler> Logger { get; }
|
||||||
|
|
||||||
|
private HashSet<string> IdsInProgress { get; } = new();
|
||||||
|
|
||||||
|
public void AddId(string id) {
|
||||||
|
this.IdsInProgress.Add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveId(string id) {
|
||||||
|
this.IdsInProgress.Remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task HandleShutdown() {
|
||||||
|
this.Logger.LogWarning("Shutdown Received.");
|
||||||
|
|
||||||
|
List<Task> tasks = [];
|
||||||
|
|
||||||
|
foreach (var id in this.IdsInProgress) {
|
||||||
|
tasks.Add(AzureStorage.DeleteFile($"{id}/generating"));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Logger.LogInformation("Ending {count} in-progress generations.", tasks.Count);
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Submodule DungeonMapRandomizer updated: 5e0deadf55...d424505677
Reference in New Issue
Block a user