Skip to content

fix: write tabs session file atomically to survive unclean shutdown - #2356

Open
jorgeecardona wants to merge 2 commits into
Guake:masterfrom
jorgeecardona:crash-safe-save-tabs
Open

jorgeecardona wants to merge 2 commits into
Guake:masterfrom
jorgeecardona:crash-safe-save-tabs

Conversation

@jorgeecardona

Copy link
Copy Markdown
Collaborator

save_tabs truncated session.json and rewrote it in place. A crash or power loss during the write left a half-written file. On the next start restore_tabs treated it as broken and moved it to .bak, so the saved tabs were lost exactly when they were needed.

  • Write to session.json.tmp, flush + os.fsync, then os.replace onto session.json. The replace is atomic, so a restore sees either the old complete file or the new one.
  • fsync the directory after the replace, so the rename itself survives a power cut.
  • Test: checks that no .tmp file is left, the result is valid JSON, and fsync runs for both the file and the directory.

All 31 tests pass locally; black and flake8 report no issues. A reno note is included.

🤖 Generated with Claude Code

save_tabs truncated session.json in place, so a crash or power loss
mid-write left a half-written file that restore_tabs then discarded as
broken, losing the saved tabs in exactly the abnormal-shutdown case.

Dump to a sibling .tmp file, flush and os.fsync it, then os.replace onto
session.json. os.replace is atomic, so restore always sees either the
previous complete file or the new one.
os.replace is atomic, but the rename lives in the directory entry. Until
the directory is synced, a power loss right after a save can bring back
the previous session.json. It is complete but stale, so the tabs saved
last are lost. fsync the directory so the new file is durable.
Copilot AI balanced review requested due to automatic review settings October 10, 2026 15:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Temporary-path construction breaks custom filenames containing directory components, and the test does not exercise interrupted writes.

2 open findings
What changed in this PR

Makes tab-session persistence crash-safe through atomic replacement and filesystem syncing.

Changes:

  • Writes sessions through a temporary file and atomically replaces the destination.
  • Adds atomic-save coverage and a release note.
File Description
guake/​guake_app.py Implements atomic, durable session saving.
guake/​tests/​test_guake.py Tests the new save path.
releasenotes/​notes/​crash-safe-save-tabs-adf00755bead6ea5.yaml Documents the fix.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread guake/guake_app.py
# it, then os.replace() onto the real path. os.replace is atomic, so a
# restore always sees either the previous complete file or the new one.
# Finally fsync the directory so the rename itself survives power loss.
tmp_file = session_file.with_name(f"{filename}.tmp")
Comment thread guake/tests/test_guake.py
Comment on lines +195 to +200
assert fsync.call_count == 2
assert os.path.exists("/foobar/session.json")
assert not os.path.exists("/foobar/session.json.tmp")
with open("/foobar/session.json", encoding="utf-8") as f:
config = json.load(f)
assert "schema_version" in config
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