feat(coordinator): running state, worker liveness, request-size limits

Polish pass hardening the queue and closing plan gaps.

- Task state machine gains `running`: the first heartbeat moves a task from
  leased to running (migrations 0006/0007 add the enum value and extend the
  lease-integrity check). verifyLease, ExpireLease, the reaper SQL, and job
  progress all treat leased and running alike.
- Worker liveness: a heartbeat from a registered worker (UUID worker_id) bumps
  its last_heartbeat_at online; a second background reaper marks workers offline
  after WORKER_OFFLINE_AFTER of silence (RunReaper generalized to RunPeriodic).
- Request-size limits: JSON bodies capped at 1 MiB; dataset/artifact uploads
  capped at MAX_UPLOAD_BYTES (default 1 GiB) via http.MaxBytesReader.
- Tests cover the running transition, liveness + offline reaper (unit over
  memstore and integration over Postgres).
This commit is contained in:
Efremenko Arhip
2026-07-23 17:36:26 +03:00
parent 4fc3c69fdf
commit 3b41455b20
23 changed files with 332 additions and 32 deletions
+10 -2
View File
@@ -69,13 +69,15 @@ func (uc *ClaimTask) Execute(ctx context.Context, in ClaimTaskInput) (*domain.Cl
type RenewLease struct {
tasks TaskRepository
workers WorkerRepository
tx TxManager
clock Clock
leaseDuration time.Duration
}
func NewRenewLease(tasks TaskRepository, tx TxManager, clock Clock, leaseDuration time.Duration) *RenewLease {
return &RenewLease{tasks: tasks, tx: tx, clock: clock, leaseDuration: leaseDuration}
func NewRenewLease(tasks TaskRepository, workers WorkerRepository, tx TxManager,
clock Clock, leaseDuration time.Duration) *RenewLease {
return &RenewLease{tasks: tasks, workers: workers, tx: tx, clock: clock, leaseDuration: leaseDuration}
}
// Execute is a read-modify-write, so it runs inside a transaction with the row
@@ -101,6 +103,12 @@ func (uc *RenewLease) Execute(ctx context.Context, in RenewLeaseInput) (*domain.
if err != nil {
return nil, err
}
// Best-effort worker liveness, outside the task transaction so it can never
// fail the heartbeat. Only registered workers (a UUID worker_id) are tracked.
if id, perr := uuid.Parse(in.WorkerID); perr == nil {
_ = uc.workers.Touch(ctx, id, uc.clock.Now())
}
return &claimed, nil
}