Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

A gateway updates a chosen worker, incompatible ones included, and routes around it meanwhile

Abierto
#838 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
5/5
Tiempo estimado
Más de una semana
Aptitud para principiantes
35/100
Tipo de issue
Nueva funcionalidad
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
node.js, typescript
Área
api, backend, cli, devtools

Línea de trabajo

Start with src/gateway/dispatcher.test.ts and the drain tests at lines 322–331, then trace worker.drain through src/gateway/dispatcher.ts, src/gateway/worker-registry.ts, and src/gateway/routing/stages/takes-requests.ts. Read the contract and CLI entry points listed in the scope, then run pnpm test:changed. Done means the specified worker and gateway update flows, routing and drain behavior, CLI output, HTTP routes, and docs are covered and CI passes.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

task:ready

Part of #825.

Scope

From a gateway, an operator updates one chosen worker, including one that shows incompatible. The ways in are simlock worker update <id> [--version X] [--force] [--detach], POST /v1/workers/{id}/update, and the client's updateWorker. The gateway routes no leases to an updating worker and never touches the drain set. The worker list shows each worker's version, its latest version and its update stage. The gateway updates itself with simlock update --gateway, with no lease wait.

Shared values: worker.update, the worker view fields, HTTP routes, WORKER_NOT_CONNECTED, the routing reason updating, the "newer" rule (read), events (read), the protocol version.

In short

simlock worker update 3f81a2c4 follows the update on that worker. It ends at mac-studio-2 (3f81a2c4) is running 0.4.0, or at Update failed (<stage>): <message>. Meanwhile, simlock worker list adds -- simlock 0.3.0 (0.4.0 available) -- updating to 0.4.0: waiting for 2 leases to that worker's line.

sequenceDiagram
  participant O as simlock worker update
  participant G as gateway
  participant W as worker (old)
  O->>G: worker.update {workerId}
  alt disconnected
    G-->>O: WORKER_NOT_CONNECTED
  else connected or incompatible
    G->>W: daemon.update (frozen; passes the degraded client)
    W-->>G: { started, update: waiting }
    G->>G: view.update set, routing skips it
    G-->>O: answer
    loop 30 s tick, and on daemon.update-* events
      G->>W: status.get, or daemon.update {query} when incompatible
    end
    Note over O: O polls worker.list every 2 s
    Note over G: worker restarts and dials back
    G-->>O: view version 0.4.0, update null: running 0.4.0
  end
Input Today After
simlock worker update 3f81a2c4 Unknown worker command: update follows to … is running <v>
simlock worker update nope — UNKNOWN_WORKER
simlock worker update <disconnected id> — WORKER_NOT_CONNECTED: worker <id> is not connected
simlock worker update <id> --detach — Updating mac-studio-2 (3f81a2c4) 0.3.0 → 0.4.0. Follow it with simlock worker list.
simlock worker update <id>, already latest — mac-studio-2 (3f81a2c4) is already on the latest (0.4.0)
simlock worker update on a worker daemon — UNSUPPORTED_IN_WORKER_MODE
simlock update --gateway — installs and restarts at once; a request in that gap fails as in any gateway restart

If this task goes wrong, a user would notice a worker kept out of rotation after its update, leases sent to an updating worker, or an operator's drain undone.

Technical spec

Facts
# Fact Source
F1 The gateway keeps the worker's client even when status.get answers PROTOCOL_VERSION_UNSUPPORTED, and marks the link incompatible src/gateway/worker-link.ts:165-167, 198, 210-221
F2 The degraded client lets daemon.update through (#836) #836 Tests
F3 The 30 s tick refreshes every link, incompatible ones included; WORKER_CALL_TIMEOUT_MS (10 s) bounds view reads only src/gateway/service.ts:287; src/gateway/worker-link.ts:44, 184
F4 A refresh merges into the old view; an absent key keeps its value (the new fields are never absent) src/gateway/worker-registry.ts:345-365
F5 The routing filter skips a worker not connected, then a drained one; reasons in COULD_TAKE_LATER keep a request waiting src/gateway/routing/stages/takes-requests.ts:14-15; src/gateway/fleet-coordinator.ts:117-122
F6 A worker answer other than NO_CAPACITY, PROVISION_FAILED or a cannot-serve code ends a fleet request worker-failed src/gateway/fleet-coordinator.ts:1053-1096
F7 worker.drain/remove handlers are the models; their tests are in src/gateway/dispatcher.test.ts:322-331 and src/daemon/gateway-fleet.e2e.test.ts; a worker answers worker.* writes with UNSUPPORTED_IN_WORKER_MODE src/gateway/dispatcher.ts:228-231, 375-397; src/daemon/dispatcher.ts:327-329
F8 worker.* inputs use workerId: z.string().min(1); UNKNOWN_WORKER exists, WORKER_NOT_CONNECTED doesn't src/contract/operations.ts:753; src/contract/errors.ts:120-124
F9 Waiting-reason texts are Records over the reason enum: GATEWAY_REASON_TEXT in the CLI and WORKER_WHY_TEXT in the console src/cli/commands/list.ts:62-76; ui/src/model/waiting.ts:96-108
F10 The worker list line is id (label): state -- capacity, leases[…] src/cli/format.ts:5-41
F11 The worker routes are at app.ts:661-691, declared by #821 src/http/app.ts:661-691; #821
F12 The gateway's own daemon object is built in aggregate.ts and the gateway dispatcher src/gateway/aggregate.ts:67; src/gateway/dispatcher.ts:317
F13 isNewerVersion is #837's #837
Modules touched

Searches: rg "drained" in src/gateway, rg "GatewayWaitingReason" (contract, cli list, ui waiting, pipeline), rg "formatWorkers", rg "\"worker\\.drain\"".

Files: src/contract/{operations,schemas,errors,worker-view,protocol}.ts, operations.test.ts; src/gateway/{dispatcher,worker-link,worker-registry,service,aggregate,test-support}.ts, src/gateway/routing/{pipeline.ts,stages/takes-requests.ts}, src/gateway/AGENTS.md; src/daemon/{main,dispatcher}.ts (the gateway role of daemon.update, wired through main.ts); src/simlock-client/{client,types}.ts, src/admin/index.ts; src/cli/commands/{worker,list}.ts, src/cli/format.ts; ui/src/model/waiting.ts (the reason text only); src/http/app.ts; docs CLI.md, HTTP-API.md, CLIENT.md.

Contract and event changes

This PR bumps the protocol by one.

  • worker.update: admin, write. Input { workerId: z.string().min(1), version?, force? } with #836's bounds. The answer is #836's answer plus workerId. On the uplink it's bounded at 60 s. At the limit, the caller gets WORKER_NOT_CONNECTED with worker <id> did not answer within 60 s; check simlock worker list, and the view catches up on the next read.
  • For an incompatible worker, daemon.update {query} runs on the 30 s tick and right after worker.update. A failed query keeps the last view.
  • Worker view: update, lastUpdateFailure, latestVersion, each null when there's nothing to show.
  • Waiting reason updating. Its text is Updating in both Records. It's in COULD_TAKE_LATER, since the worker comes back by itself.
  • WORKER_NOT_CONNECTED (domain, 409, exit 2): worker <id> is not connected.
  • A status re-read also runs on daemon.update-started and daemon.update-failed (beside WORKER_VIEW_CATALOG_EVENTS) and right after worker.update answers.
  • daemon.update on a gateway: no lease wait, straight to installing; fleet admission is not changed.
Other code on the same state

Searches: rg "setDrained" (registry, dispatcher), rg "takesRequests" (routing 3).

  • The drain set and update are separate. An update never calls setDrained, and a drain or undrain during an update changes only the drain set.
  • A lease-event refresh re-reads status.get, and its update: null clears the view.
Failure modes
Call Outcome What happens Limit, counted from
daemon.update over the uplink ok view updated from the answer 60 s
same error reply passed to the caller (UPDATE_*) —
same uplink closed / timeout WORKER_NOT_CONNECTED / the timeout error 60 s
simlock worker update follow view version = to, update null, connected or incompatible … is running <to>, exit 0 none; Ctrl-C stops following
same lastUpdateFailure.to = to Update failed (<stage>): <message>, exit 1 —
gateway's own install fails lastUpdateFailure in its status.get, it keeps serving npm's 10 min
gateway's new version doesn't start #835 rolls back; the gateway's start reads <root>/gateway/update-result.json (#836's readResultFile, wired in startGatewayDaemon) and reports it 2 min (#835)

A gateway restart mid-update rebuilds each view from the worker, which still reports update.

Rules in play
  • ADR 0042 §2: "worker.update … sends daemon.update over that worker's uplink".
  • ADR 0042 §3 and ADR 0005 §9: the update never touches the drain set, and "only undrain ends it".
  • ADR 0005 §31: an incompatible worker "is never dispatched to". Only the frozen call goes out.
Tests

Seams: src/gateway/dispatcher.test.ts and src/daemon/gateway-fleet.e2e.test.ts with ScriptedWorkerClient (src/gateway/test-support.ts:271, given a pass-through for daemon.update when degraded), modelled on the drain tests. Routes go through src/http/app.test.ts, the gateway's own update through src/daemon/main.test.ts, and the CLI through src/cli/index.test.ts.

  • Operator calls worker.update on the gateway: connected (worker gets daemon.update, view update set); incompatible (frozen call sent, view filled from query); disconnected (WORKER_NOT_CONNECTED); unknown id (UNKNOWN_WORKER); POST /v1/workers/{id}/update (202, 200, 403 for an agent token).
  • Gateway routes a lease while a worker is updating: an updating worker is skipped (updating, the request waits); the update ends (update: null clears the view, the worker is picked again); an operator-drained worker stays drained after its update.
  • Operator updates the gateway itself: no wait with fleet leases held; a failed install shows in its status.get; a rolled-back gateway reports the failure at start.
  • Operator runs simlock worker update and simlock worker list: follows to … is running <v>; failure (exit 1); already latest; --detach; on a worker daemon (UNSUPPORTED_IN_WORKER_MODE); the list line shows simlock <v> (<latest> available) only when isNewerVersion, and updating to <v>: <stage>.

Done when

  • pnpm test:changed passes (it runs the unit tests under src/gateway src/daemon src/cli src/http src/contract, src/daemon/gateway-fleet.e2e.test.ts included).
  • CI's check job passes.
  • docs/CLI.md documents simlock worker update and the new list fields; docs/HTTP-API.md documents the routes.

Out of scope

  • Updating every worker in one call. Queuing an update for a disconnected worker. The console (#839).

Depends on

  • #837
  • #821

Written by an agent.

Lenguaje dominante
TypeScript
Estrellas
20
Forks
2
Merge medio
8 h
PR fusionados (30 d)
316

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de callstackincubator/simlock

Todos los issues de callstackincubator/simlock

Issues similares

Más issues de TypeScript

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.