fix(docs): correct fabricated API/behavior claims in the locks docs
docs/API.md's Locks section described a timeoutMs request param, a 408-timeout response, and field names (acquiredAt/acquiredMs) that don't exist anywhere in the actual routes.js — the server is single-shot and never blocks; timing out is a CLI-only concept. Also fixes the section being spliced into the middle of the pre-existing Sessions heading and its content. docs/LANES.md said the owner file stores an epoch in milliseconds (it's seconds), that the CLI polls every ~1s (it's ~2s), that the lane card shows a "waiting" state (no such server-side concept exists, only who currently holds), and included a fabricated "manually transfer a lock's holder identity" procedure that also contradicted the never-touch-the-lock-directory etiquette rule stated right above it.
This commit is contained in:
+26
-59
@@ -397,95 +397,62 @@ resolved path is confined to the lane's log directory after `realpath`, so a
|
||||
name from the request can never escape it; anything else is `404 ENOLOG`. A lane
|
||||
with no slot returns `{"available": false}`.
|
||||
|
||||
### Sessions
|
||||
|
||||
|
||||
### Locks
|
||||
|
||||
Cross-lane named locks (`server/lib/named-lock.js`) — the OTHER axis from a
|
||||
lane's own internal serialization: a name held by one lane at a time, across
|
||||
ALL lanes, for a step that thrashes the shared machine (a build, an e2e run).
|
||||
Every route here is **single-shot** — it never blocks waiting for a lock to
|
||||
free up. A caller that wants to wait polls `POST /:name/acquire` itself (the
|
||||
`ccam lock acquire` CLI does exactly this, printing a status line every ~60s
|
||||
of continued waiting); the server never orchestrates that loop.
|
||||
|
||||
#### List locks
|
||||
|
||||
```http
|
||||
GET /api/locks
|
||||
```
|
||||
|
||||
Returns all currently-held named locks:
|
||||
Every currently-held lock:
|
||||
|
||||
```json
|
||||
{
|
||||
"locks": [
|
||||
{
|
||||
"name": "build",
|
||||
"holder": "lane3",
|
||||
"acquiredAt": 1722702012345,
|
||||
"acquiredMs": 1722702012345
|
||||
}
|
||||
]
|
||||
"locks": [{ "name": "build", "holder": "lane3", "since": 1722702012, "ageSec": 41 }]
|
||||
}
|
||||
```
|
||||
|
||||
Each lock has a `name`, the current `holder` (defaults to `lane<slot>`), and `acquiredAt` / `acquiredMs` (the millisecond timestamp when acquired). If no locks are held, the array is empty.
|
||||
`since` is a Unix **seconds** timestamp; `ageSec` is derived at read time. An
|
||||
empty array means nothing is held.
|
||||
|
||||
#### Acquire a lock
|
||||
|
||||
```http
|
||||
POST /api/locks/:name/acquire
|
||||
{ "holder": "lane3" }
|
||||
```
|
||||
|
||||
Request body:
|
||||
`holder` is **required** — this route does not default it; `ccam lock
|
||||
acquire` resolves `lane<slot>` for the calling lane and sends it explicitly.
|
||||
A holder older than `LOCK_MAX_HOLD` (default 2700s, floored at 300s — see
|
||||
`docs/LANES.md#cross-lane-named-locks`) is broken automatically before the
|
||||
attempt.
|
||||
|
||||
```json
|
||||
{
|
||||
"holder": "lane3",
|
||||
"timeoutMs": 30000
|
||||
}
|
||||
```
|
||||
|
||||
`holder` defaults to the calling lane (`lane<slot>`) if omitted. `timeoutMs` is optional; without it, the request waits indefinitely. Returns **200** with the acquired lock object:
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "build",
|
||||
"holder": "lane3",
|
||||
"acquiredAt": 1722702012345
|
||||
}
|
||||
```
|
||||
|
||||
or **408** if the timeout elapses before the lock becomes free:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "ETIMEOUT",
|
||||
"waited": 30000,
|
||||
"currentHolder": "lane1",
|
||||
"message": "Lock held by lane1, timeout elapsed"
|
||||
}
|
||||
```
|
||||
- **200** `{ "acquired": true }` — lock claimed.
|
||||
- **409** `{ "acquired": false, "holder": "lane1", "since": 1722701900, "ageSec": 150 }` — already held by someone else (fresh enough not to be broken).
|
||||
- **400** `{ "error": { "code": "EBADHOLDER", "message": "holder is required" } }` — missing/empty `holder`.
|
||||
|
||||
#### Release a lock
|
||||
|
||||
```http
|
||||
POST /api/locks/:name/release
|
||||
{ "holder": "lane3" }
|
||||
```
|
||||
|
||||
Request body:
|
||||
- **200** `{ "ok": true }` — released.
|
||||
- **409** `{ "error": { "code": "ENOTHOLDER", "message": "...", "currentHolder": "lane1" } }` — `holder` doesn't match the current owner; a release never trusts its caller, same rule every other destructive path in this project follows.
|
||||
- **404** `{ "error": { "code": "ENOLOCK", "message": "no such lock: build" } }` — nothing by that name is held.
|
||||
|
||||
```json
|
||||
{
|
||||
"holder": "lane3"
|
||||
}
|
||||
```
|
||||
|
||||
`holder` defaults to `lane<slot>` if omitted. Returns **200** with the released lock object, or **409** if the holder does not match:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "ENOTHOLDER",
|
||||
"name": "build",
|
||||
"expectedHolder": "lane3",
|
||||
"actualHolder": "lane1",
|
||||
"message": "Lock is held by lane1, not lane3"
|
||||
}
|
||||
```
|
||||
### Sessions
|
||||
|
||||
#### List Sessions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user