Skip to content

Commit 6339cfc

Browse files
authored
fix: Docker内运行通过页面修改配置 Failed to update system configuration: [Errno 16] Device or resource busy (ZhuLinsen#306)
1 parent 4c791a2 commit 6339cfc

1 file changed

Lines changed: 28 additions & 3 deletions

File tree

src/core/config_manager.py

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
from __future__ import annotations
44

5+
import errno
56
import hashlib
7+
import logging
68
import os
79
import re
810
import threading
@@ -13,6 +15,9 @@
1315
from dotenv import dotenv_values
1416

1517
_ASSIGNMENT_PATTERN = re.compile(r"^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*)$")
18+
_FALLBACK_REWRITE_ERRNOS = {errno.EBUSY, errno.EXDEV}
19+
20+
logger = logging.getLogger(__name__)
1621

1722

1823
class ConfigManager:
@@ -64,7 +69,7 @@ def apply_updates(
6469
sensitive_keys: Set[str],
6570
mask_token: str,
6671
) -> Tuple[List[str], List[str], str]:
67-
"""Apply updates into `.env` file using atomic replace semantics."""
72+
"""Apply updates into `.env` file using atomic replace when possible."""
6873
with self._lock:
6974
current_values = self.read_config_map()
7075
mutable_updates: Dict[str, str] = {}
@@ -90,7 +95,7 @@ def apply_updates(
9095
return list(mutable_updates.keys()), skipped_masked, self.get_config_version()
9196

9297
def _atomic_upsert(self, updates: Dict[str, str]) -> None:
93-
"""Write updates with temp file + fsync + rename strategy."""
98+
"""Write updates with atomic rename and in-place fallback for mounted files."""
9499
lines = self._read_lines()
95100
key_to_index = self._find_last_key_indexes(lines)
96101

@@ -115,7 +120,27 @@ def _atomic_upsert(self, updates: Dict[str, str]) -> None:
115120
file_obj.flush()
116121
os.fsync(file_obj.fileno())
117122

118-
os.replace(temp_path, self._env_path)
123+
try:
124+
os.replace(temp_path, self._env_path)
125+
except OSError as exc:
126+
if exc.errno not in _FALLBACK_REWRITE_ERRNOS:
127+
raise
128+
129+
logger.warning(
130+
"Atomic replace for .env failed with errno=%s, falling back to in-place rewrite",
131+
exc.errno,
132+
)
133+
self._rewrite_in_place(content)
134+
finally:
135+
if temp_path.exists():
136+
temp_path.unlink()
137+
138+
def _rewrite_in_place(self, content: str) -> None:
139+
"""Rewrite `.env` content in place when rename is unsupported by mount type."""
140+
with self._env_path.open("w", encoding="utf-8", newline="\n") as file_obj:
141+
file_obj.write(content)
142+
file_obj.flush()
143+
os.fsync(file_obj.fileno())
119144

120145
def _read_lines(self) -> List[str]:
121146
if not self._env_path.exists():

0 commit comments

Comments
 (0)