Progress reporting: read partclone's snapshot, do not consume it - #184
Merged
Merged
Conversation
fog.statusreporter treated /tmp/status.fog as a stream. It is a snapshot: FOG's own partclone patch rewrites the file whole, fopen(..., "w"), on every UI tick. Three defects came out of that one misreading, and none of them is visible from the imaging screen. It truncated the file after every read. partclone rewrites it anyway, so the truncation bought nothing -- and a read landing between the truncation and the next tick found the file EMPTY and posted an empty status, which service/progress.php discards in silence because it requires six @-separated fields and says nothing when it does not get them. It also meant nothing else could ever read that file, because this consumed it. It posted every three seconds whether or not the value had changed. Each post costs the server a host lookup, a task load, an image load and a save, multiplied by every client in a multicast, to re-send a value the server already held. And `[[ -z $mac ]] && continue` jumped PAST the usleep at the bottom of the loop, so a task reaching it span at 100% CPU for its whole duration. funcs.sh recovers the MAC now (fogproject#1767) so nothing reaches it, which is why the shape is pinned rather than left to be rediscovered. Now: read without truncating, send only a line carrying the six fields the server needs, and post only when the value has changed -- with a keepalive so a client that has gone quiet and one that has stalled stay distinguishable in the server's access log. That last part is not hypothetical; forum topic 18247 is a deploy whose diagnosis is a 48-second hole between its final progress post and its reboot. Polling and posting are deliberately separate rates. partclone is asked for a tick a second (-f 1), so reading once a second means the value that goes out is current; posting keeps the old three-second floor, so the server's worst case is what it has always been. Transport now matches reportToServer(): --max-time, so a wedged connection cannot stall the loop with nothing on screen, and --data-urlencode per field. -Lks is unchanged; -k trusts the certificate a default FOG install presents, and that is a decision about every call in FOS rather than about this one. The wire format is untouched, so this is safe against every server version in the field. FOS is not branched -- one init runs against 1.5 and 1.6 alike -- and nothing here needs a server that understands anything new. tests/checks/status-reporting.sh runs the shipped loop for real against a sandbox fixture, with the timing constants rewritten so the assertions take seconds. Each case was confirmed to fail against the defect it covers, reintroduced one at a time. The spin is measured from /proc rather than `ps -o time=`: the loop burns its time in the tail it forks, so ps reports 00:00:00 for a process eating a core, and the assertion passed on the very defect it exists to catch until that was fixed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fog.statusreportertreated/tmp/status.fogas a stream. It is a snapshot: FOG's own partclone patch rewrites the file whole —fopen(..., "w")— on every UI tick. Three defects came out of that one misreading, and none of them is visible from the imaging screen.It truncated the file after every read. partclone rewrites it anyway, so the truncation bought nothing — and a read landing between the truncation and the next tick found the file empty and posted an empty status, which
service/progress.phpdiscards in silence (it requires six@-separated fields and says nothing when it does not get them). It also meant nothing else could ever read that file, because this consumed it.It posted every three seconds whether or not the value had changed. Each post costs the server a host lookup, a task load, an image load and a save, multiplied by every client in a multicast. See the measurement below for how much of that is actually redundant — less than this originally claimed.
[[ -z $mac ]] && continuejumped past theusleepat the bottom of the loop, so a task reaching it span at 100% CPU for its whole duration.funcs.shrecovers the MAC now (FOGProject/fogproject#1767) so nothing reaches it, which is why the shape is pinned rather than left to be rediscovered.What changes
Read without truncating. Send only a line carrying the six fields the server needs. Post only when the value has changed, with a keepalive so a client that has gone quiet and one that has stalled stay distinguishable in the server's access log — that last part is not hypothetical, forum topic 18247 is a deploy whose whole diagnosis is a 48-second hole between its final progress post and its reboot.
Polling and posting are deliberately separate rates. partclone is asked for a tick a second (
-f 1), so reading once a second means the value that goes out is current; posting keeps the old three-second floor, so the server's worst case is what it has always been.Transport now matches
reportToServer():--max-time, so a wedged connection cannot stall the loop with nothing on screen, and--data-urlencodeper field.-Lksis unchanged —-ktrusts the certificate a default FOG install presents, and that is a decision about every call in FOS rather than about this one.Compatibility
The wire format is untouched. FOS is not branched — one init runs against 1.5 and 1.6 alike — and nothing here needs a server that understands anything new.
Verification
tests/checks/status-reporting.shruns the shipped loop for real against a sandbox fixture, with the timing constants rewritten so the assertions take seconds. Every case was confirmed red against the defect it covers, reintroduced one at a time:continuerestoredThe spin is measured from
/procrather thanps -o time=: the loop burns its time in thetailit forks, sopsreports00:00:00for a process eating a core, and the assertion passed on the very defect it exists to catch until that was fixed.Full suite: 23 passed, 0 failed.
Measured on a booted init
Built x64 (
build.sh -nfa x64), confirmed the built image carries the change by extractingbin/fog.statusreporterback out of it, deployed it to a 1.6 lab server and ran a real deploy:win11-fostest, Single Disk - Resizable + Everything — the same image configuration as forumtopic 18247 — onto an empty 80 GB disk. The client fetched the new init (31,520,852 bytes, the new
build's exact size).
pct100,Post_Stage3.php200percent46.93,bpm10.44,timeElapsed00:01:53,timeRemaining00:02:07,dataCopied19.594 GiB,dataTotal40.762 GiB — all six, so the payload is understood and not merely acceptedThe claim above was too strong, and this run is why. During an active partclone restore the
value genuinely changes every second (
-f 1), so the 3-second floor means the new code posts atthe same rate the old one did. 84 posts against roughly 97 for the old code over the same window
is about a 13% reduction, not the large one implied. The saving is real but it lives entirely in
the phases where nothing is moving: one 9-second gap where a partition boundary stalled the value,
and 26 seconds of silence between the last progress post and Post_Stage3 — the post-restore
work — where the old code would have sent about eight identical updates.
That 26-second window is worth looking at twice. It is the same window that, in forum topic 18247,
is a 48-second hole with a reboot at the end of it. The keepalive is 30 seconds and this run did
not reach it, which is the behaviour intended: quiet because there was nothing to say, and the
server's next word from that client was the completion.
The correctness fixes — no truncation of the producer's file, no empty or malformed payloads, a
request timeout, no sleep-skipping
continue— are not rate questions and are unaffected by this.Everything above was also re-verified green after the lab run.
🤖 Generated with Claude Code