mirror of
https://github.com/ajisaacs/OpenNest.git
synced 2026-11-03 17:01:38 -05:00
Large nests tie up the workstation that runs them. In Database mode, Auto Nest can now queue the job on the nest server, where a worker container solves one job at a time under its own CPU and memory limits. The job has no time limit, so a throttled worker finds the same nest a fast one does, only later. - Server: /api/nest-jobs queue (schema v15). The worker holds a job on a heartbeat lease. When the lease lapses, the job goes back to the queue, or fails after three attempts. Only one job runs at a time. Packages and results are opaque to the HTTP server, which stays off Engine and IO. - Worker: OpenNest.Server.Worker in the same image (OPENNEST_ROLE=worker), reaching the server over HTTP only. It records each engine answer with RecordingNestingEngine and gives its job back on shutdown. - Desktop: "Nest on the server" in the Auto Nest dialog, for built-in engines only. The client replays the recorded answers with RemoteNestSubmission, so validation, the review and Keep run locally against the client's own drawings, exactly as after a local run. A drawing edited after sending refuses the result. If the server cannot take the job, the user may nest locally instead. - compose.server.yaml adds the throttled worker service. The Dockerfile, .dockerignore, CI paths and the deploy script now include the worker's projects. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
2.0 KiB
C#
56 lines
2.0 KiB
C#
namespace OpenNest.Server.Worker;
|
|
|
|
/// <summary>
|
|
/// Runs <see cref="NestJobWorker"/> until the host stops: straight on to the next job after
|
|
/// one finishes, otherwise every <see cref="WorkerOptions.PollInterval"/>. Touches
|
|
/// <see cref="WorkerOptions.AliveFile"/> each round and on each heartbeat of a running job;
|
|
/// the container health check reads it.
|
|
/// </summary>
|
|
public sealed class NestJobWorkerService : BackgroundService
|
|
{
|
|
private readonly NestJobWorker _worker;
|
|
private readonly WorkerOptions _options;
|
|
private readonly ILogger<NestJobWorkerService> _logger;
|
|
|
|
public NestJobWorkerService(NestJobWorker worker, WorkerOptions options, ILogger<NestJobWorkerService> logger)
|
|
{
|
|
_worker = worker;
|
|
_options = options;
|
|
_logger = logger;
|
|
_worker.Alive = Touch;
|
|
}
|
|
|
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
{
|
|
_logger.LogInformation("Nest worker {Id} polling {Server}.", _worker.WorkerId, _options.ServerUrl);
|
|
while (!stoppingToken.IsCancellationRequested)
|
|
{
|
|
Touch();
|
|
var ran = false;
|
|
try
|
|
{
|
|
ran = await _worker.RunOnceAsync(stoppingToken);
|
|
}
|
|
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
|
|
{
|
|
return;
|
|
}
|
|
catch (Exception ex) when (ex is IOException or HttpRequestException or TaskCanceledException)
|
|
{
|
|
_logger.LogWarning("Nest server unavailable: {Message}", ex.Message);
|
|
}
|
|
|
|
if (ran)
|
|
continue;
|
|
try { await Task.Delay(_options.PollInterval, stoppingToken); }
|
|
catch (OperationCanceledException) { return; }
|
|
}
|
|
}
|
|
|
|
private void Touch()
|
|
{
|
|
try { File.WriteAllText(_options.AliveFile, DateTime.UtcNow.ToString("o")); }
|
|
catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) { }
|
|
}
|
|
}
|