Repository navigation
Conversation
fa0fe3f to
69ef146
Compare
f0650c2 to
12eb6e2
Compare
69ef146 to
aeac33f
Compare
12eb6e2 to
1bb1896
Compare
aeac33f to
04b5e9d
Compare
1bb1896 to
651b18b
Compare
04b5e9d to
b1c7167
Compare
651b18b to
3c76b62
Compare
b1c7167 to
5d7a713
Compare
3c76b62 to
474cfab
Compare
5d7a713 to
3d338cd
Compare
474cfab to
6b0cfa0
Compare
3d338cd to
8b74a6b
Compare
6b0cfa0 to
f0c458f
Compare
8b74a6b to
de02c29
Compare
f0c458f to
1b647b2
Compare
de02c29 to
826c383
Compare
1b647b2 to
ec0d8cc
Compare
|
Review results. One reviewer per dimension, each reading the others' findings. All seven dimensions have zero blockers on the latest round. Fixed along the way: the Handle lock and failed-Append refusal, with context checks before each step. The timer-loop test moved here from #213 and now also fails if a Fire or Tick errors or no timer fires. Known limit: that test is reliable under |
f7b771b to
4ee6c01
Compare
9f0db01 to
ad2b6c6
Compare
4ee6c01 to
26b512f
Compare
ad2b6c6 to
831afc6
Compare
7fd91fe to
2ab1add
Compare
831afc6 to
aef9065
Compare
WithEventData payloads are recorded and supplied again by Recover and StateAt, through the additive state.FireEventData. The live Fire now uses the decoded payload too, so the live run and a replay can't differ. An error payload is rebuilt from its message. A payload that can't be recorded, or is over WithMaxEventDataBytes, refuses the Fire before the kernel runs. FileStore refuses a Record too large for it to read back. Relates to #214. Signed-off-by: Ray Karnes <ray.karnes@stablekernel.com>
Recovery derived the next step from the restored Trace count when a checkpoint had compacted the tail. A tick barrier consumes an ordinal but leaves no Trace, so the count fell short and the next Fire reused a recorded step. The checkpoint envelope now records the step that follows it. A checkpoint written before this change keeps the old fallback. Relates to #214. Signed-off-by: Ray Karnes <ray.karnes@stablekernel.com>
Fire, Tick, RunService and DeliverToActor now hold a lock for the whole step, so a host's timer loop no longer loses acknowledged steps. A step that applied to the kernel but wasn't appended leaves the instance ahead of the journal, so the Handle refuses further steps with ErrHandleFailed until it's recovered. Relates to #214. Signed-off-by: Ray Karnes <ray.karnes@stablekernel.com>
2ab1add to
67d9182
Compare
Review of the rebased change (494c78a..2f8b57c)The change is rebased onto main after #232 merged, plus one fix commit (2f8b57c). All seven dimensions are confirmed, so the PR is ready. The reviewers (AI agents, one per dimension, each starting with a clean context) could only read code: they traced the code paths but couldn't run tests. I ran the tests and the mutation checks myself.
Fixed in 2f8b57c
Rejected: pre-existing on main, fixed higher in the stackThese come from code this PR doesn't change. Fixing them here would mean fixing more than the bottom PR of the stack.
Pre-existing, possibly not tracked yet
Non-blockers left as they are
VerificationOn
|
What this change does
Two #214 fixes in the same code path. Rebased onto main now that #232 has merged.
Fire,Tick,RunServiceandDeliverToActorhold a lock for the whole step, so they're safe from different goroutines. This is the contract in my answer to question 10 on #214, which is a working answer, not a ruling.Append. A step that applied to the kernel but may not have been recorded fails the Handle. That call and every later one return the newErrHandleFailed, wrapping the cause, until a new Handle is recovered.nextStepnow advances right after the append, so a failed checkpoint no longer makes the next step conflict.Tick,RunServiceandDeliverToActorreturnctx.Err()once they hold the lock, so a deadline that ended in the lock queue changes nothing. Before,Tickconsumed a due timer the kernel then refused.Please check these:
Appendstays inline, on the session's single owner goroutine. If it moves off that goroutine, the lock scope and this failure handling change.state.ServiceRunnerruns and settles in one call, so I couldn't narrow it here.Handledoc says so.Instance()returns the live instance outside the lock.ErrFenced, a newer owner holds the instance, soRecoverwon't help. The docs say to stop.Append, a service whose result wasn't recorded is in flight again on the recovered Handle, soRunServiceruns it a second time. AnInvokeActoractor restarts from its initial state, and aSpawnactor doesn't return.Known and left open:
TickorDeliverToActorcan still journal a step the kernel refused, and a canceled delivery can makeRecoverfail. An already-ended context no longer reaches that path.Storethat panics mid-step skips the failure latch, if the host recovers the panic.Runner.Firecalls on one id aren't serialized. The doc says so.Fireleaves a Trace only in the live instance, so a later snapshot can differ byte for byte.Why
A host's timer loop racing
Firelost acknowledged steps, and a failedAppendleft the live instance ahead of the journal.Relates to: #214
Evidence
On
GOTOOLCHAIN=go1.27.3, indurable:go test ./...TestRunner_FireDuringTimerLoop_LosesNoAcknowledgedSteps,TestRunner_FireAfterFailedAppend_MatchesJournal, and the failure-latch and ended-context tests forRunServiceandDeliverToActorinhandle_failure_test.go, each checked to fail with its guard removed. The timer-loop test now also fails if a Fire or Tick errors or no timer fires. It's reliable under-race; a plain run can pass by luckgo test -race ./...golangci-lintChecklist
git commit -s) per the DCOtype: subject)mage checkpasses locally (lint + race tests + govulncheck): not run as a whole, and it cannot pass until the rest of the chain lands