mirror of
https://github.com/nestriness/nestri.git
synced 2026-09-19 17:25:19 +03:00
fix(api): a box gets one run, and a stopped run keeps no address
Two invariants the session endpoint stated but did not hold. A box runs one thing at a time. `POST /session` read `activeForBox` and refused when something was already running, but the read and the insert are two statements with nothing between them: two requests that both saw "nothing is running" each got a row, and the job poll then handed the host the same box to start twice. Demonstrated at 2 rows and 2 jobs from one box. That is the failure the state claim exists to prevent, one step earlier, and it takes the same answer — a partial unique index on the predicate the read asks about, so the database refuses the second insert. `Session.request` turns that refusal into the same 409 in the same words, so a caller cannot tell which of the two caught it. The migration resolves any existing duplicates before creating the index, keeping each box's newest unstopped run because that is the one a person is waiting on, and stopping the rest rather than deleting them. Separately, a run that reached `ended` or `failed` kept the last ticket it published. Publishing a new one is already refused, so the stale address was both the only ticket a client could read for a dead run and the one nothing was allowed to replace — and a client that polls would dial it. Terminal transitions now clear it, in `setState` as well as in the compare-and-set, so the invariant does not depend on which writer stopped the run. Seven tests, each checked against the unfixed code first. The published descriptions for the ticket field and the read endpoint now say that a stopped run has no address.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
-- A box runs one thing at a time, and now the database is what says so.
|
||||
--
|
||||
-- `POST /session` reads `session.activeForBox` and refuses when something is
|
||||
-- already running, but the read and the insert are two statements. Two requests
|
||||
-- that both read "nothing is running" before either inserts each get a row, and
|
||||
-- `/machine/jobs` then hands the host the same box to start twice. A partial
|
||||
-- unique index on the same predicate the read asks about makes the second
|
||||
-- insert fail instead. ref(d-0048)
|
||||
--
|
||||
-- The index cannot be created while any box already has two unstopped runs, so
|
||||
-- the duplicates are resolved first. Keeping the newest is the only choice that
|
||||
-- matches what a person saw: their most recent request is the one they are
|
||||
-- waiting on. The older rows are stopped rather than deleted, because a session
|
||||
-- is the billing unit and rows that were once real do not vanish from it.
|
||||
--
|
||||
-- `ended` and not `failed`: nothing about these runs failed. They were work
|
||||
-- nobody picked up, and `failed` carries a reason there is none of.
|
||||
UPDATE "session" s
|
||||
SET "state" = 'ended', "time_stopped" = now()
|
||||
WHERE s."time_stopped" IS NULL
|
||||
AND s."time_deleted" IS NULL
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM "session" newer
|
||||
WHERE newer."box_id" = s."box_id"
|
||||
AND newer."time_stopped" IS NULL
|
||||
AND newer."time_deleted" IS NULL
|
||||
AND (newer."time_created", newer."id") > (s."time_created", s."id")
|
||||
);--> statement-breakpoint
|
||||
|
||||
CREATE UNIQUE INDEX "session_box_active_unique" ON "session" USING btree ("box_id") WHERE time_stopped is null and time_deleted is null;
|
||||
2299
packages/core/migrations/meta/0008_snapshot.json
Normal file
2299
packages/core/migrations/meta/0008_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -57,6 +57,13 @@
|
||||
"when": 1788460224524,
|
||||
"tag": "0007_box_session_team_notnull",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 8,
|
||||
"version": "7",
|
||||
"when": 1788547836146,
|
||||
"tag": "0008_session_one_active_run_per_box",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user