fix: docker 默认 runner 支持 env + 并发开通竞态复用

翻转实弹暴露的两个 #88 缺口:
- _docker_default_runner 不接受 env,_call_runner 遇 env 即 loud fail,
  导致 sub2api 路径下所有 docker 任务失败(xcmapi 路径不传 env,
  此前未暴露)。默认 runner 现接受 env 并与进程环境合并后传给
  docker CLI 子进程(-e <名> 由此解析,值绝不进 argv)。
- 同一用户两任务并发开通时建号撞 email 已存在:现轮询复用
  胜出者入库的行,等不到再原错抛出。
测试:sub2api 16、docker 沙箱 29;全量 cloud 789(4 跳过)、
host 65、deploy 19 全绿。
This commit is contained in:
host committed 2026-10-27 17:59:04 +08:00
1 parent 99efa93bd3
commit 4936db3c47
4 files changed
+90 -5

No files matched your search

+16 -2
View File
@@ -309,7 +309,8 @@ def ensure_user_gateway_key(conn, user_id, admin=None, request=None):
入库失败时尽力清理刚建的 key 与账号(避免孤儿 key),再抛原错;
授余额失败同样走这条清理(余额为 0 的 key 在网关侧直接 403,
留着没有意义)。调用方负责 conn 的提交语义:本函数在入库后
留着没有意义)。同一用户并发开通时建号撞 email 已存在,
则轮询复用胜出者入库的行。调用方负责 conn 的提交语义:本函数在入库后
commit(开通是独立事务,不跟调用方的业务事务绑在一起)。
"""
ensure_gateway_keys_table(conn)
@@ -324,7 +325,20 @@ def ensure_user_gateway_key(conn, user_id, admin=None, request=None):
email = "codex-%s@codex.local" % user_id
username = "codex-" + str(user_id)[:12]
password = secrets.token_hex(16)
sub_id = admin.create_user(email, password, username)
try:
sub_id = admin.create_user(email, password, username)
except Sub2apiError as exc:
if "already exists" not in str(exc).lower():
raise
# 并发开通竞态:同一用户的另一路已建号(email 唯一),
# 等它入库后复用那一行;等不到则原错抛出(任务失败后
# 可重跑,孤儿号场景下重跑仍撞号需管理员介入清理)。
for _ in range(5):
time.sleep(2)
row = get_user_gateway_key(conn, user_id)
if row is not None:
return row["api_key"]
raise
key_id = None
try:
user_token, refresh = user_login(origin, email, password,