Skip to content

Commit 3519e08

Browse files
committed
merge: SquidStd.Workers.Manager + workers e2e integration test (sub-project #4)
2 parents 028f927 + 3ed8b15 commit 3519e08

30 files changed

Lines changed: 1254 additions & 0 deletions

SquidStd.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<Project Path="src/SquidStd.Templating/SquidStd.Templating.csproj" />
2222
<Project Path="src/SquidStd.Workers.Abstractions/SquidStd.Workers.Abstractions.csproj" />
2323
<Project Path="src/SquidStd.Workers/SquidStd.Workers.csproj" />
24+
<Project Path="src/SquidStd.Workers.Manager/SquidStd.Workers.Manager.csproj" />
2425
</Folder>
2526
<Folder Name="/tests/">
2627
<Project Path="tests/SquidStd.Tests/SquidStd.Tests.csproj" />

docs/articles/toc.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@
3838
href: scripting-lua.md
3939
- name: SquidStd.Templating
4040
href: templating.md
41+
- name: SquidStd.Workers.Abstractions
42+
href: workers-abstractions.md
43+
- name: SquidStd.Workers
44+
href: workers.md
45+
- name: SquidStd.Workers.Manager
46+
href: workers-manager.md
4147
- name: Serialization
4248
href: serialization.md
4349
- name: Scheduler
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[!include[](../../src/SquidStd.Workers.Abstractions/README.md)]

docs/articles/workers-manager.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[!include[](../../src/SquidStd.Workers.Manager/README.md)]

docs/articles/workers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[!include[](../../src/SquidStd.Workers/README.md)]
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<p align="center">
2+
<img src="https://raw.githubusercontent.com/tgiachi/squid-std/main/assets/icon.png" alt="SquidStd" width="120" height="120" />
3+
</p>
4+
5+
<h1 align="center">SquidStd.Workers.Abstractions</h1>
6+
7+
<p align="center">
8+
<a href="https://www.nuget.org/packages/SquidStd.Workers.Abstractions/"><img src="https://img.shields.io/nuget/v/SquidStd.Workers.Abstractions.svg" alt="NuGet" /></a>
9+
<img src="https://img.shields.io/nuget/dt/SquidStd.Workers.Abstractions.svg" alt="Downloads" />
10+
<a href="https://tgiachi.github.io/squid-std/articles/workers-abstractions.html"><img src="https://img.shields.io/badge/docs-DocFX-1390A3.svg" alt="docs" /></a>
11+
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="license" />
12+
</p>
13+
14+
Shared, dependency-free contracts between the SquidStd worker runtime and manager: the job descriptor, the
15+
worker heartbeat, the manager-side worker view, the worker status enum, and the conventional channel names.
16+
17+
## Install
18+
19+
```bash
20+
dotnet add package SquidStd.Workers.Abstractions
21+
```
22+
23+
## Key types
24+
25+
| Type | Purpose |
26+
|------|---------|
27+
| `JobRequest` | A job (name + string parameters) enqueued by the manager and consumed by a worker. |
28+
| `WorkerHeartbeat` | Liveness signal a worker publishes (status, active jobs, max concurrency). |
29+
| `WorkerInfo` | The manager-side view of a worker, folded from heartbeats. |
30+
| `WorkerStatusType` | `Idle` / `Busy` / `Offline`. |
31+
| `WorkerChannels` | Default jobs-queue and heartbeat-topic names. |
32+
33+
## License
34+
35+
MIT — part of [SquidStd](https://github.com/tgiachi/squid-std).
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using SquidStd.Workers.Abstractions;
2+
3+
namespace SquidStd.Workers.Manager.Data.Config;
4+
5+
/// <summary>
6+
/// Configuration for the worker manager (config section "workerManager").
7+
/// </summary>
8+
public sealed class WorkerManagerConfig
9+
{
10+
/// <summary>Seconds without a heartbeat before a worker is considered Offline. Falls back to 30 when not positive.</summary>
11+
public int OfflineTimeoutSeconds { get; set; } = 30;
12+
13+
/// <summary>Seconds between offline sweeps. Falls back to 10 when not positive.</summary>
14+
public int SweepIntervalSeconds { get; set; } = 10;
15+
16+
/// <summary>Queue jobs are enqueued onto.</summary>
17+
public string JobQueue { get; set; } = WorkerChannels.JobQueue;
18+
19+
/// <summary>Topic heartbeats are collected from.</summary>
20+
public string HeartbeatTopic { get; set; } = WorkerChannels.HeartbeatTopic;
21+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace SquidStd.Workers.Manager.Data;
2+
3+
/// <summary>
4+
/// Request body for enqueuing a job via the manager's HTTP surface.
5+
/// </summary>
6+
/// <param name="JobName">Logical name of the job.</param>
7+
/// <param name="Parameters">Optional string key/value arguments; treated as empty when null.</param>
8+
public sealed record EnqueueJobRequest(string JobName, IReadOnlyDictionary<string, string>? Parameters);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using SquidStd.Core.Interfaces.Events;
2+
using SquidStd.Workers.Abstractions.Types;
3+
4+
namespace SquidStd.Workers.Manager.Data.Events;
5+
6+
/// <summary>
7+
/// Published on the event bus when a worker's status changes: discovered (<paramref name="OldStatus" /> null),
8+
/// gone Offline (via the sweep), or returned (Offline → Idle/Busy).
9+
/// </summary>
10+
/// <param name="WorkerId">The worker whose status changed.</param>
11+
/// <param name="OldStatus">Previous status, or <c>null</c> when the worker was just discovered.</param>
12+
/// <param name="NewStatus">The new status.</param>
13+
public sealed record WorkerStatusChangedEvent(string WorkerId, WorkerStatusType? OldStatus, WorkerStatusType NewStatus) : IEvent;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Http.HttpResults;
3+
using SquidStd.Workers.Abstractions.Data;
4+
using SquidStd.Workers.Manager.Data;
5+
using SquidStd.Workers.Manager.Interfaces;
6+
7+
namespace SquidStd.Workers.Manager.Endpoints;
8+
9+
/// <summary>
10+
/// Typed minimal-API handlers for the worker manager surface. Static so they can be unit-tested directly.
11+
/// </summary>
12+
public static class WorkerManagerEndpoints
13+
{
14+
/// <summary>Lists all known workers.</summary>
15+
public static Ok<IReadOnlyCollection<WorkerInfo>> GetWorkers(IWorkerRegistry registry)
16+
=> TypedResults.Ok(registry.GetAll());
17+
18+
/// <summary>Returns a single worker, or 404 when unknown.</summary>
19+
public static Results<Ok<WorkerInfo>, NotFound> GetWorker(string id, IWorkerRegistry registry)
20+
{
21+
var info = registry.Get(id);
22+
23+
return info is null ? TypedResults.NotFound() : TypedResults.Ok(info);
24+
}
25+
26+
/// <summary>Enqueues a job; 400 on a blank name, 503 when the queue is unavailable.</summary>
27+
public static async Task<Results<Accepted, BadRequest<string>, ProblemHttpResult>> EnqueueJob(
28+
EnqueueJobRequest request,
29+
IJobScheduler scheduler,
30+
CancellationToken cancellationToken)
31+
{
32+
if (string.IsNullOrWhiteSpace(request.JobName))
33+
{
34+
return TypedResults.BadRequest("JobName is required.");
35+
}
36+
37+
try
38+
{
39+
await scheduler.EnqueueAsync(request.JobName, request.Parameters ?? new Dictionary<string, string>(), cancellationToken);
40+
41+
return TypedResults.Accepted((string?)null);
42+
}
43+
catch (Exception)
44+
{
45+
return TypedResults.Problem(statusCode: StatusCodes.Status503ServiceUnavailable, detail: "Job queue unavailable.");
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)