Merge remote-tracking branch 'origin/main' into HEAD
This commit is contained in:
commit
fb5df0efde
2 changed files with 74 additions and 5 deletions
|
|
@ -27,9 +27,20 @@ from ..router_config import RouterProfileConfig
|
||||||
from .base_adapter import BaseProviderAdapter, ErrorCategory, ErrorClassification
|
from .base_adapter import BaseProviderAdapter, ErrorCategory, ErrorClassification
|
||||||
|
|
||||||
|
|
||||||
import threading
|
# Глобального мьютекса здесь больше нет.
|
||||||
|
#
|
||||||
_AGY_INVOCATION_LOCK = threading.RLock()
|
# Он был введён в 50fde5f, чтобы защитить ГЛОБАЛЬНУЮ подмену учётных данных
|
||||||
|
# gemini:antigravity от гонки между подпроцессами. С тех пор подмена перестала
|
||||||
|
# быть глобальной: agy_subprocess пишет в profile_dir/.gemini/oauth_creds.json,
|
||||||
|
# а HOME, USERPROFILE, HOMEPATH и HOMEDRIVE подменяются на каталог профиля.
|
||||||
|
# Общего состояния между профилями не осталось, и мьютекс охранял пустоту.
|
||||||
|
#
|
||||||
|
# Стоил он при этом дорого: измерено, что три параллельных вызова по одной
|
||||||
|
# секунде занимали 3.01 с. То есть из десяти аккаунтов Antigravity
|
||||||
|
# одновременно работал ровно один, чем обесценивалась вся мультиаккаунтность.
|
||||||
|
#
|
||||||
|
# Ограничение одновременности остаётся за LeaseManager, который считает лизы
|
||||||
|
# ПО ПРОФИЛЮ и настраивается через max_concurrency.
|
||||||
|
|
||||||
|
|
||||||
def get_profile_env_dir(profile_id: str) -> Path:
|
def get_profile_env_dir(profile_id: str) -> Path:
|
||||||
|
|
@ -77,7 +88,6 @@ class AntigravityAdapter(BaseProviderAdapter):
|
||||||
profile_id=profile.profile_id,
|
profile_id=profile.profile_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
with _AGY_INVOCATION_LOCK:
|
|
||||||
res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id)
|
res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id)
|
||||||
else:
|
else:
|
||||||
res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id)
|
res = agy_generate(req, custom_env=custom_env, profile_id=profile.profile_id)
|
||||||
|
|
|
||||||
|
|
@ -77,3 +77,62 @@ def test_credential_restoration_on_subprocess_exception(tmp_path, monkeypatch):
|
||||||
|
|
||||||
# Must be cleanly restored
|
# Must be cleanly restored
|
||||||
assert True
|
assert True
|
||||||
|
|
||||||
|
def test_antigravity_profiles_run_in_parallel():
|
||||||
|
"""Вызовы разных профилей Antigravity идут одновременно, а не по очереди.
|
||||||
|
|
||||||
|
В adapters/antigravity_adapter.py жил модульный _AGY_INVOCATION_LOCK. Он был
|
||||||
|
введён, когда подмена учётных данных gemini:antigravity была ГЛОБАЛЬНОЙ, и
|
||||||
|
защищал её от гонки. Позже подмена стала попрофильной — agy_subprocess
|
||||||
|
пишет в profile_dir/.gemini/oauth_creds.json, а HOME, USERPROFILE, HOMEPATH
|
||||||
|
и HOMEDRIVE подменяются на каталог профиля, — но мьютекс остался.
|
||||||
|
|
||||||
|
Цена измерялась: три параллельных вызова по одной секунде занимали 3.01 с.
|
||||||
|
То есть из десяти подключённых аккаунтов одновременно работал ровно один.
|
||||||
|
|
||||||
|
Тест удерживает это свойство: три вызова по 0.4 с должны уложиться заметно
|
||||||
|
быстрее суммы, и каждый обязан идти со своим изолированным HOME.
|
||||||
|
"""
|
||||||
|
import threading
|
||||||
|
import time
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import antigravity_provider.router.adapters.antigravity_adapter as adapter_mod
|
||||||
|
|
||||||
|
auth = {"token": {"access_token": "a", "refresh_token": "r", "expires_at": 9999999999}}
|
||||||
|
|
||||||
|
class _Profile:
|
||||||
|
def __init__(self, pid):
|
||||||
|
self.profile_id = pid
|
||||||
|
self.preferred_models = ["gemini-3.7-flash"]
|
||||||
|
self.provider = "antigravity"
|
||||||
|
|
||||||
|
homes = []
|
||||||
|
|
||||||
|
def _slow(req, custom_env=None, profile_id=None):
|
||||||
|
env = custom_env or {}
|
||||||
|
homes.append(env.get("HOME") or env.get("USERPROFILE"))
|
||||||
|
time.sleep(0.4)
|
||||||
|
return {"content": "ok"}
|
||||||
|
|
||||||
|
with patch.object(adapter_mod, "agy_generate", side_effect=_slow), patch.object(
|
||||||
|
adapter_mod.ProfileAuthManager, "load_profile_auth", return_value=auth
|
||||||
|
):
|
||||||
|
started = time.time()
|
||||||
|
threads = [
|
||||||
|
threading.Thread(target=lambda pid=f"ag-w{i}": adapter_mod.AntigravityAdapter().invoke(
|
||||||
|
_Profile(pid), {"messages": []}
|
||||||
|
))
|
||||||
|
for i in range(1, 4)
|
||||||
|
]
|
||||||
|
for t in threads:
|
||||||
|
t.start()
|
||||||
|
for t in threads:
|
||||||
|
t.join()
|
||||||
|
elapsed = time.time() - started
|
||||||
|
|
||||||
|
assert elapsed < 0.9, (
|
||||||
|
f"вызовы разных профилей сериализуются ({elapsed:.2f} с при трёх по 0.4 с); "
|
||||||
|
"вероятно вернулся глобальный мьютекс"
|
||||||
|
)
|
||||||
|
assert len({h for h in homes if h}) == 3, f"профили делят HOME: {homes}"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue