mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
fix(e2e): stage the seeded device id per thread, not per process
Build 232 took two compat cells red with a FileNotFoundError renaming `.claude.json.197` onto `.claude.json`. `run_claude_models_parallel` drives several models from one process, so a pid-suffixed staged name is shared between threads: one thread renamed the file the other was still writing, and the loser died on a path that no longer existed. mkstemp in the same directory gives a name that is unique per thread as well as per process, and the rename stays atomic.
This commit is contained in:
parent
2481146727
commit
9421b26bf6
2 changed files with 26 additions and 4 deletions
|
|
@ -33,7 +33,7 @@ from typing import List, Tuple
|
|||
|
||||
import pytest
|
||||
|
||||
from claude_code.cli_driver import _stable_cli_state, run_claude
|
||||
from claude_code.cli_driver import _FIXED_CLI_USER_ID, _seed_cli_identity, _stable_cli_state, run_claude
|
||||
from claude_code.rate_limiter import RateLimiter
|
||||
|
||||
_STUB_REPLY = {
|
||||
|
|
@ -141,3 +141,20 @@ def test_concurrent_cells_do_not_collide_on_the_pinned_session(
|
|||
assert codes == [0, 0, 0, 0]
|
||||
assert bodies, "the CLI sent no request to the stub, so there is nothing to compare"
|
||||
assert set(Counter(bodies).values()) == {4}
|
||||
|
||||
|
||||
def test_seeding_the_device_id_survives_threads_racing_on_the_same_directory(tmp_path: Path) -> None:
|
||||
"""`run_claude_models_parallel` drives several models from one process, so the
|
||||
seed's staged file has to be unique per thread and not merely per process."""
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
seeded = config_dir / ".claude.json"
|
||||
|
||||
for _round in range(20):
|
||||
seeded.unlink(missing_ok=True)
|
||||
with ThreadPoolExecutor(max_workers=16) as pool:
|
||||
for outcome in [pool.submit(_seed_cli_identity, str(config_dir)) for _ in range(16)]:
|
||||
outcome.result()
|
||||
|
||||
assert json.loads(seeded.read_text(encoding="utf-8"))["userID"] == _FIXED_CLI_USER_ID
|
||||
assert sorted(entry.name for entry in config_dir.iterdir()) == [".claude.json"]
|
||||
|
|
|
|||
|
|
@ -143,7 +143,12 @@ def _seed_cli_identity(config_dir: str) -> None:
|
|||
`userID`, and sends them in `metadata.user_id` forever after, so the value
|
||||
is stable for exactly as long as that file lives. Pinning it, and the
|
||||
session id passed beside it, costs nothing: both feed abuse detection
|
||||
rather than quota, caching or continuity."""
|
||||
rather than quota, caching or continuity.
|
||||
|
||||
The staged name has to be unique per *thread*, not per process:
|
||||
`run_claude_models_parallel` drives several models from one process, so a
|
||||
pid-suffixed name lets one thread rename the file another is still
|
||||
writing, and the loser dies on a missing path."""
|
||||
path = os.path.join(config_dir, ".claude.json")
|
||||
try:
|
||||
with open(path, encoding="utf-8") as handle:
|
||||
|
|
@ -151,8 +156,8 @@ def _seed_cli_identity(config_dir: str) -> None:
|
|||
return
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
staged = f"{path}.{os.getpid()}"
|
||||
with open(staged, "w", encoding="utf-8") as handle:
|
||||
handle_fd, staged = tempfile.mkstemp(dir=config_dir, prefix=".claude.json.")
|
||||
with os.fdopen(handle_fd, "w", encoding="utf-8") as handle:
|
||||
json.dump({"userID": _FIXED_CLI_USER_ID}, handle)
|
||||
os.replace(staged, path)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue