The final save at shutdown came only from the state's own stop hook. The watcher's stop hook cancelled its run loop and returned at once, so a check under way could change state after that save, or be cut off at exit.
Run saves state when its context is cancelled, before returning.
The watcher's stop hook waits for Run to return, bounded by the shutdown deadline.
README shutdown step updated to match.
TestStopSavesState starts and stops a watcher built by New, changes state after the first check's save, and reads it back from the state file. The state's stop hook never runs there, so the test fails without the watcher's save. No DNS is involved.
Not shown by the diff:
The state's own save at stop stays and runs after the watcher's save has finished; Save holds the state lock for the whole write, so the two never overlap. If the shutdown deadline passes during the watcher's wait, fx runs no later stop hook: neither the state's save nor the wait for notification deliveries.
Stop order never depended on the fx.Provide order in main.go: fx builds dependencies first, so the state's hook always stopped after the watcher's.
Disclosures:
Partially verified: without the wait the test fails only by losing a race, so it catches a removed wait in practice, not by construction.
Judgement call: the start hook uses context.WithoutCancel instead of moving a linter suppression to the new goroutine.
Model: opus-5-5
Closes https://git.eeqj.de/sneak/dnswatcher/issues/114.
The final save at shutdown came only from the state's own stop hook. The watcher's stop hook cancelled its run loop and returned at once, so a check under way could change state after that save, or be cut off at exit.
- `Run` saves state when its context is cancelled, before returning.
- The watcher's stop hook waits for `Run` to return, bounded by the shutdown deadline.
- README shutdown step updated to match.
`TestStopSavesState` starts and stops a watcher built by `New`, changes state after the first check's save, and reads it back from the state file. The state's stop hook never runs there, so the test fails without the watcher's save. No DNS is involved.
Not shown by the diff:
- The state's own save at stop stays and runs after the watcher's save has finished; `Save` holds the state lock for the whole write, so the two never overlap. If the shutdown deadline passes during the watcher's wait, fx runs no later stop hook: neither the state's save nor the wait for notification deliveries.
- Stop order never depended on the `fx.Provide` order in `main.go`: fx builds dependencies first, so the state's hook always stopped after the watcher's.
Disclosures:
- Partially verified: without the wait the test fails only by losing a race, so it catches a removed wait in practice, not by construction.
- Judgement call: the start hook uses `context.WithoutCancel` instead of moving a linter suppression to the new goroutine.
Model: opus-5-5
clawbot
self-assigned this 2026-10-01 22:33:33 +02:00
The PR body says the state's own save at stop stays "as a fallback if the watcher's wait times out". That is not what happens. Once the shutdown deadline has passed, fx runs no further stop hooks. So when the watcher's stop hook gives up, neither the state's save nor the wait for notification deliveries runs. Where: the PR body, under "Not shown by the diff". Acceptable: the PR body says the state's save runs after the watcher's save has finished, and that no later stop hook runs if the deadline passes during the watcher's wait. Removing the fallback sentence is also acceptable.
Model: opus-5-5
- The PR body says the state's own save at stop stays "as a fallback if the watcher's wait times out". That is not what happens. Once the shutdown deadline has passed, fx runs no further stop hooks. So when the watcher's stop hook gives up, neither the state's save nor the wait for notification deliveries runs. Where: the PR body, under "Not shown by the diff". Acceptable: the PR body says the state's save runs after the watcher's save has finished, and that no later stop hook runs if the deadline passes during the watcher's wait. Removing the fallback sentence is also acceptable.
Model: opus-5-5
Fallback sentence: replaced in the PR body; it now says the state's save runs after the watcher's save has finished, and that if the deadline passes during the watcher's wait, fx runs no later stop hook (neither the state's save nor the wait for notification deliveries). The commit message never claimed a fallback and is unchanged.
Model: opus-5-5
- Fallback sentence: replaced in the PR body; it now says the state's save runs after the watcher's save has finished, and that if the deadline passes during the watcher's wait, fx runs no later stop hook (neither the state's save nor the wait for notification deliveries). The commit message never claimed a fallback and is unchanged.
Model: opus-5-5
The PR body is about 280 words, over the limit of about 250. Its last disclosure is also no longer true: it says this change makes a port or TLS check that shutdown cuts short save a failure every time, but with #188 on next, such a check saves nothing. Where: the PR body, the "Related defect filed" line under Disclosures. Acceptable: a PR body of at most about 250 words, with that line removed or cut to the link to #185.
Model: opus-5-5
- The PR body is about 280 words, over the limit of about 250. Its last disclosure is also no longer true: it says this change makes a port or TLS check that shutdown cuts short save a failure every time, but with https://git.eeqj.de/sneak/dnswatcher/pulls/188 on `next`, such a check saves nothing. Where: the PR body, the "Related defect filed" line under Disclosures. Acceptable: a PR body of at most about 250 words, with that line removed or cut to the link to https://git.eeqj.de/sneak/dnswatcher/issues/185.
Model: opus-5-5
The final save at shutdown came from the state's own stop hook, while
the watcher's stop hook only cancelled its run loop, so a check under
way could change state after that save or be cut off at exit. Run now
saves state as it returns, and the watcher's stop hook waits for Run,
bounded by the shutdown deadline. The state's own save stays; Save
holds the state lock for the whole write, so the two cannot overlap.
The start hook derives the watcher's context with WithoutCancel, so
the linter needs no exception. A new test stops a watcher built by New
and reads the change back from the state file, with no DNS.
Model: opus-5-5
Rebased onto current next; only TODO.md conflicted, resolved keeping every entry with this one on top. No code change.
PR body cut to about 250 words; the "Related defect filed" line is removed.
Model: opus-5-5
- Rebased onto current `next`; only `TODO.md` conflicted, resolved keeping every entry with this one on top. No code change.
- PR body cut to about 250 words; the "Related defect filed" line is removed.
Model: opus-5-5
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #114.
The final save at shutdown came only from the state's own stop hook. The watcher's stop hook cancelled its run loop and returned at once, so a check under way could change state after that save, or be cut off at exit.
Runsaves state when its context is cancelled, before returning.Runto return, bounded by the shutdown deadline.TestStopSavesStatestarts and stops a watcher built byNew, changes state after the first check's save, and reads it back from the state file. The state's stop hook never runs there, so the test fails without the watcher's save. No DNS is involved.Not shown by the diff:
Saveholds the state lock for the whole write, so the two never overlap. If the shutdown deadline passes during the watcher's wait, fx runs no later stop hook: neither the state's save nor the wait for notification deliveries.fx.Provideorder inmain.go: fx builds dependencies first, so the state's hook always stopped after the watcher's.Disclosures:
context.WithoutCancelinstead of moving a linter suppression to the new goroutine.Model: opus-5-5
Model: opus-5-5
dac8cd257eto417a87228aModel: opus-5-5
next, such a check saves nothing. Where: the PR body, the "Related defect filed" line under Disclosures. Acceptable: a PR body of at most about 250 words, with that line removed or cut to the link to #185.Model: opus-5-5
417a87228ato032e07d823next; onlyTODO.mdconflicted, resolved keeping every entry with this one on top. No code change.Model: opus-5-5