Skip to content

updater: the entry after a torn log line is no longer lost with it - #435

Open
rudycelekli wants to merge 1 commit into
pollen-robotics:mainfrom
rudycelekli:fix/updater-journal-torn-tail
Open

rudycelekli wants to merge 1 commit into
pollen-robotics:mainfrom
rudycelekli:fix/updater-journal-torn-tail

Conversation

@rudycelekli

Copy link
Copy Markdown

What's wrong

Journal::append (updater/src/journal.rs) opens update-log.jsonl with append(true) and writes the serialised entry plus \n. The doc comment expects power loss mid-append ("a torn final line is tolerated on read"), and read_all does skip a torn tail. The problem is the next append. A torn tail has no newline, so the next entry lands on the same line:

{"at":1,"component":"daemon",{"at":…,"component":"daemon","to":"1.1.0","outcome":{"rolled_back":…}}\n

That combined line parses as neither entry, so read_all drops it. The first outcome recorded after the power cut is lost along with the torn one.

The entry after a power cut is often the one that matters most: the boot-counter revert, or a gate failure recorded as RolledBack. Journal::known_bad reads those entries to keep rollback from landing on a release that already failed its gate, so losing one can send the board back to a known-bad version.

The fix

Before writing, append checks whether the file is non-empty and does not end in \n. If so, it starts a new line first. The file is opened with read(true) as well, so a small helper can read the last byte. Appends still go to the end, because the file is in append mode. The torn line itself is still skipped, as before.

Test

journal::tests::an_entry_appended_after_a_torn_line_is_kept: append one entry, write a torn half-line after it (the same way tolerates_torn_final_line does), then append a RolledBack entry for 1.1.0. It asserts that both whole entries read back, and that known_bad("daemon") is [1.1.0].

  • On main: fails, because recent() returns 1 entry ("the entry after the torn line was lost").
  • With the fix: passes.
  • Mutation check: I removed only the line.insert(0, b'\n'), and that named test failed. Then I put it back.

Checks (macOS, aarch64)

  • cargo test -p updater: 399 passed, 0 failed (lib 244, bins and integration suites 155)
  • cargo clippy -p updater --all-targets -- -D warnings: clean
  • cargo fmt -p updater --check: clean

🤖 Generated with Claude Code

`Journal::append` opened the update log in append mode and wrote the
new line straight after whatever was there. After a power cut mid-append
the file ends in a torn line with no newline, so the next entry was glued
onto it, and the combined line parsed as neither. `read_all` skips it, so
the first outcome recorded after the cut was lost. That is often the
rollback the cut caused, which `known_bad` uses to keep `rollback` off a
release that already failed its gate.

Now a new line is started first when the file does not end in one.

Assisted-by: Claude:claude-opus-5-5

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant