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:
@@ -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):
|
||||||
|
|||||||
Reference in New Issue
Block a user