fix(session): tolerate unsupported directory fsync
This commit is contained in:
committed by
chengyongru
parent
9db0d9f3c9
commit
4a79cbb6e7
@@ -1,6 +1,7 @@
|
||||
"""Session management for conversation history."""
|
||||
|
||||
import base64
|
||||
import errno
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
@@ -688,12 +689,16 @@ class SessionManager:
|
||||
if fsync:
|
||||
# fsync the directory so the rename is durable.
|
||||
# On Windows, opening a directory with O_RDONLY raises
|
||||
# PermissionError — skip the dir sync there (NTFS
|
||||
# journals metadata synchronously).
|
||||
# PermissionError; some shared filesystems allow the open but
|
||||
# reject directory fsync with EINVAL.
|
||||
with suppress(PermissionError):
|
||||
fd = os.open(str(path.parent), os.O_RDONLY)
|
||||
try:
|
||||
try:
|
||||
os.fsync(fd)
|
||||
except OSError as exc:
|
||||
if exc.errno != errno.EINVAL:
|
||||
raise
|
||||
finally:
|
||||
os.close(fd)
|
||||
except BaseException:
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
@@ -55,6 +57,46 @@ class TestSaveFsync:
|
||||
manager.save(session)
|
||||
mock_fsync.assert_not_called()
|
||||
|
||||
def test_save_ignores_unsupported_directory_fsync(
|
||||
self, manager: SessionManager
|
||||
) -> None:
|
||||
"""Shared filesystems may open directories but reject directory fsync."""
|
||||
session = manager.get_or_create("test:unsupported-directory-fsync")
|
||||
session.add_message("user", "hello")
|
||||
directory_fd = 987654
|
||||
with (
|
||||
patch("nanobot.session.manager.os.open", return_value=directory_fd) as open_dir,
|
||||
patch(
|
||||
"nanobot.session.manager.os.fsync",
|
||||
side_effect=[None, OSError(errno.EINVAL, "Invalid argument")],
|
||||
),
|
||||
patch("nanobot.session.manager.os.close") as close_dir,
|
||||
):
|
||||
manager.save(session, fsync=True)
|
||||
|
||||
assert manager._get_session_path(session.key).exists()
|
||||
open_dir.assert_called_once_with(str(manager.sessions_dir), os.O_RDONLY)
|
||||
close_dir.assert_called_once_with(directory_fd)
|
||||
|
||||
def test_save_propagates_other_directory_fsync_errors(
|
||||
self, manager: SessionManager
|
||||
) -> None:
|
||||
"""Only EINVAL is an expected unsupported-directory-fsync result."""
|
||||
session = manager.get_or_create("test:directory-fsync-io-error")
|
||||
directory_fd = 987654
|
||||
with (
|
||||
patch("nanobot.session.manager.os.open", return_value=directory_fd),
|
||||
patch(
|
||||
"nanobot.session.manager.os.fsync",
|
||||
side_effect=[None, OSError(errno.EIO, "I/O error")],
|
||||
),
|
||||
patch("nanobot.session.manager.os.close") as close_dir,
|
||||
pytest.raises(OSError, match="I/O error"),
|
||||
):
|
||||
manager.save(session, fsync=True)
|
||||
|
||||
close_dir.assert_called_once_with(directory_fd)
|
||||
|
||||
|
||||
class TestFlushAll:
|
||||
"""Verify flush_all re-saves all cached sessions with fsync."""
|
||||
|
||||
Reference in New Issue
Block a user