fix: 修复调度器只选前5个用户的bug,改为全量轮转

问题根因:
1. SQL查询加了 .limit(max_concurrent),导致只有数据库前5条用户参与互动
2. 额外的 random.random() < 0.6 过滤进一步减少了执行用户数

修复方案:
- 查询所有已登录用户(去掉 SQL LIMIT)
- 按最后互动时间升序排序,最久未互动的用户优先
- 前1/3名额给最久未互动用户(优先权),其余随机补充
- 每轮最多执行 max_concurrent 个用户,保证公平轮转
This commit is contained in:
stefanfeng
2026-04-03 10:28:49 +08:00
parent 7203f04be6
commit 4ab8f94663

View File

@@ -130,14 +130,13 @@ class SchedulerService:
# 获取最大并发 # 获取最大并发
max_concurrent = int(await self._get_config(db, "max_concurrent_users", "5")) max_concurrent = int(await self._get_config(db, "max_concurrent_users", "5"))
# 获取已登录、启用的用户 # 获取所有已登录、启用的用户(不加 LIMIT确保所有用户公平参与
query = select(VirtualUser).where( result = await db.execute(
select(VirtualUser).where(
VirtualUser.status == 2, VirtualUser.status == 2,
VirtualUser.is_enabled == 1, VirtualUser.is_enabled == 1,
) )
if max_concurrent > 0: )
query = query.limit(max_concurrent)
result = await db.execute(query)
all_users = result.scalars().all() all_users = result.scalars().all()
# 没有已登录用户时,尝试登录未登录用户 # 没有已登录用户时,尝试登录未登录用户
@@ -146,25 +145,38 @@ class SchedulerService:
return return
# 检查互动间隔:过滤掉最近 min_interval 秒内已互动的用户 # 检查互动间隔:过滤掉最近 min_interval 秒内已互动的用户
now_utc = datetime.now() now_dt = datetime.now()
eligible = [] eligible = []
for u in all_users: for u in all_users:
if u.last_interact_at is None: if u.last_interact_at is None:
eligible.append(u) eligible.append(u)
else: else:
elapsed = (now_utc - u.last_interact_at).total_seconds() elapsed = (now_dt - u.last_interact_at).total_seconds()
if elapsed >= min_interval: if elapsed >= min_interval:
eligible.append(u) eligible.append(u)
if not eligible: if not eligible:
logger.debug(f"[调度] 所有用户在 {min_interval}s 内已互动,跳过本次") logger.debug(f"[调度] 所有 {len(all_users)}用户在 {min_interval}s 内已互动,跳过本次")
return return
logger.info(f"[调度] {len(eligible)}/{len(all_users)} 个用户满足间隔要求,开始互动") # 按最后互动时间升序排序:最久没互动的用户优先
eligible.sort(key=lambda u: u.last_interact_at or datetime.min)
# 随机选取用户执行互动 # 从符合条件的用户中随机选取 max_concurrent 个执行(保证公平轮转)
for user in eligible: batch_size = max_concurrent if max_concurrent > 0 else len(eligible)
if random.random() < 0.6: # 优先选最久未互动的用户前1/3其余随机补充
priority_size = max(1, batch_size // 3)
priority_users = eligible[:priority_size]
rest_users = eligible[priority_size:]
random.shuffle(rest_users)
selected = priority_users + rest_users[:max(0, batch_size - priority_size)]
logger.info(
f"[调度] 共 {len(all_users)} 个用户,{len(eligible)} 个满足间隔,"
f"本轮选取 {len(selected)} 个执行互动"
)
for user in selected:
asyncio.create_task(self._execute_user_interaction(user.id)) asyncio.create_task(self._execute_user_interaction(user.id))
async def _try_login_users(self, db): async def _try_login_users(self, db):