Skip to content

Make JsonSyncable tolerate an interrupted write - #154

Draft
hahn-kev-bot wants to merge 2 commits into
mainfrom
claude/frosty-antonelli-fbd836
Draft

hahn-kev-bot wants to merge 2 commits into
mainfrom
claude/frosty-antonelli-fbd836

Conversation

@hahn-kev-bot

Copy link
Copy Markdown
Collaborator

AI summary

Summary

FwLite's flash-drive sync will use a JsonSyncable folder, and a drive pulled mid-write leaves a client_<id>.jsonl file ending in a partial line. Before this change, that one fragment made the whole folder unsyncable (JsonException on every read), and the next append was written onto the same line as the fragment, corrupting it too.

 ReadAllCommitsAsync(file)
   for each line
-    deserialize (throws on a truncated last line)
+    if last line and file doesn't end in '\n'
+      deserialize, or skip with a warning if it fails
+    else
+      deserialize (malformed lines mid-file still throw)

 AddRangeFromSync(commits)
   lock client
+    RepairTrailingFragmentAsync(file)
+      bytes after last '\n' parse as a commit → append "\n"
+      otherwise                              → truncate to last '\n'
     read existing ids, append new commits

Dropping the fragment loses nothing: the device that wrote it still has the commit and re-sends it on the next sync, which deduplicates by commit Id. A line counts as terminated only by \n, so \r\n works and a bare trailing \r counts as unterminated.

Evidence

New tests in JsonSyncableTests, run against the old JsonSyncable.cs:

  • Before: 5 of 11 fail
    failed ReadingIgnoresTruncatedLastLine
    failed AppendingRemovesTruncatedLastLine(newline: "\n")
    failed AppendingRemovesTruncatedLastLine(newline: "\r\n")
    failed AppendingRemovesFragmentWhenFileHasNoCompleteLines
    failed UnterminatedCompleteLastLineIsKept(trailing: "")
    
    After: 11 of 11 pass. The Syncable backend suite (53 tests) also passes, and dotnet format --verify-no-changes is clean.

MalformedLineInTheMiddleThrows and TerminatedMalformedLastLineThrows pass both before and after; they guard against mid-file corruption being silently skipped.

Merge Danger

Door: two-way

Reverting restores the old behaviour. The only write-side effect is that a trailing fragment is truncated from a client file, and that fragment was unreadable anyway.

Blast Radius: contained

Only JsonSyncable changes. A file ending in a truncated line that is actually the only copy of a commit would lose that commit, but the writer appends commits it already holds, so that shouldn't happen.

Test plan

  • dotnet test src/SIL.Harmony.Tests -- --filter-class '*JsonSyncableTests'
  • dotnet test src/SIL.Harmony.Tests -- --filter-namespace 'SIL.Harmony.Tests.Syncable'
  • CI

🤖 Generated with Claude Code

hahn-kev and others added 2 commits October 26, 2026 14:53
A client file can end in a partial line when a drive is pulled mid-sync.
Reads now skip an unterminated last line that fails to parse, and appends
first truncate that fragment (or terminate it, if it is a complete commit)
so new commits land on their own line. Malformed lines elsewhere still throw.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

2 participants