chore(linter): make ruff happy

This commit is contained in:
MingxuanGame
2025-09-13 14:38:29 +00:00
parent c072dde9d5
commit ffe4c5b14d
5 changed files with 108 additions and 132 deletions

View File

@@ -1,4 +1,5 @@
"""V1 API 用户相关模型""" """V1 API 用户相关模型"""
from __future__ import annotations from __future__ import annotations
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
@@ -6,15 +7,17 @@ from pydantic import BaseModel, Field
class PlayerStatsHistory(BaseModel): class PlayerStatsHistory(BaseModel):
"""玩家 PP 历史数据""" """玩家 PP 历史数据"""
pp: list[float] = Field(default_factory=list) pp: list[float] = Field(default_factory=list)
class PlayerModeStats(BaseModel): class PlayerModeStats(BaseModel):
"""单个模式的玩家统计数据""" """单个模式的玩家统计数据"""
id: int id: int
mode: int mode: int
tscore: int # total_score tscore: int # total_score
rscore: int # ranked_score rscore: int # ranked_score
pp: float pp: float
plays: int # play_count plays: int # play_count
playtime: int # play_time playtime: int # play_time
@@ -23,10 +26,10 @@ class PlayerModeStats(BaseModel):
total_hits: int total_hits: int
replay_views: int # replays_watched_by_others replay_views: int # replays_watched_by_others
xh_count: int # grade_ssh xh_count: int # grade_ssh
x_count: int # grade_ss x_count: int # grade_ss
sh_count: int # grade_sh sh_count: int # grade_sh
s_count: int # grade_s s_count: int # grade_s
a_count: int # grade_a a_count: int # grade_a
level: int level: int
level_progress: int level_progress: int
rank: int rank: int
@@ -36,11 +39,13 @@ class PlayerModeStats(BaseModel):
class PlayerStatsResponse(BaseModel): class PlayerStatsResponse(BaseModel):
"""玩家统计信息响应 - 包含所有模式""" """玩家统计信息响应 - 包含所有模式"""
stats: dict[str, PlayerModeStats] = Field(default_factory=dict) stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
class PlayerEventItem(BaseModel): class PlayerEventItem(BaseModel):
"""玩家事件项目""" """玩家事件项目"""
userId: int userId: int
name: str name: str
mapId: int | None = None mapId: int | None = None
@@ -57,11 +62,13 @@ class PlayerEventItem(BaseModel):
class PlayerEventsResponse(BaseModel): class PlayerEventsResponse(BaseModel):
"""玩家事件响应""" """玩家事件响应"""
events: list[PlayerEventItem] = Field(default_factory=list) events: list[PlayerEventItem] = Field(default_factory=list)
class PlayerInfo(BaseModel): class PlayerInfo(BaseModel):
"""玩家基本信息""" """玩家基本信息"""
id: int id: int
name: str name: str
safe_name: str safe_name: str
@@ -93,11 +100,13 @@ class PlayerInfo(BaseModel):
class PlayerInfoResponse(BaseModel): class PlayerInfoResponse(BaseModel):
"""玩家信息响应""" """玩家信息响应"""
info: PlayerInfo info: PlayerInfo
class PlayerAllResponse(BaseModel): class PlayerAllResponse(BaseModel):
"""玩家完整信息响应 - 包含所有数据""" """玩家完整信息响应 - 包含所有数据"""
info: PlayerInfo info: PlayerInfo
stats: dict[str, PlayerModeStats] = Field(default_factory=dict) stats: dict[str, PlayerModeStats] = Field(default_factory=dict)
events: list[PlayerEventItem] = Field(default_factory=list) events: list[PlayerEventItem] = Field(default_factory=list)
@@ -105,17 +114,20 @@ class PlayerAllResponse(BaseModel):
class GetPlayerInfoResponse(BaseModel): class GetPlayerInfoResponse(BaseModel):
"""get_player_info 接口响应""" """get_player_info 接口响应"""
status: str = "success" status: str = "success"
player: PlayerStatsResponse | PlayerEventsResponse | PlayerInfoResponse | PlayerAllResponse player: PlayerStatsResponse | PlayerEventsResponse | PlayerInfoResponse | PlayerAllResponse
class PlayerCountData(BaseModel): class PlayerCountData(BaseModel):
"""玩家数量数据""" """玩家数量数据"""
online: int online: int
total: int total: int
class GetPlayerCountResponse(BaseModel): class GetPlayerCountResponse(BaseModel):
"""get_player_count 接口响应""" """get_player_count 接口响应"""
status: str = "success" status: str = "success"
counts: PlayerCountData counts: PlayerCountData

View File

@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from . import beatmap, replay, score, user, public_user # noqa: F401 from . import beatmap, public_user, replay, score, user # noqa: F401
from .router import router as api_v1_router
from .public_router import public_router as api_v1_public_router from .public_router import public_router as api_v1_public_router
from .router import router as api_v1_router
__all__ = ["api_v1_router", "api_v1_public_router"] __all__ = ["api_v1_public_router", "api_v1_router"]

View File

@@ -5,36 +5,37 @@ from typing import Literal
from app.database.lazer_user import User from app.database.lazer_user import User
from app.database.statistics import UserStatistics from app.database.statistics import UserStatistics
from app.dependencies.database import Database, get_redis from app.dependencies.database import Database, get_redis
from app.models.v1_user import (
GetPlayerInfoResponse,
PlayerStatsResponse,
PlayerEventsResponse,
PlayerInfoResponse,
PlayerAllResponse,
PlayerStatsHistory,
GetPlayerCountResponse,
PlayerCountData
)
from app.models.score import GameMode
from app.log import logger from app.log import logger
from app.router.v1.public_router import public_router, AllStrModel from app.models.score import GameMode
from app.models.v1_user import (
GetPlayerCountResponse,
GetPlayerInfoResponse,
PlayerAllResponse,
PlayerCountData,
PlayerEventsResponse,
PlayerInfoResponse,
PlayerStatsHistory,
PlayerStatsResponse,
)
from app.router.v1.public_router import public_router
from fastapi import HTTPException, Query from fastapi import HTTPException, Query
from fastapi.responses import JSONResponse
from sqlmodel import select from sqlmodel import select
async def _create_player_mode_stats(session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]): async def _create_player_mode_stats(
session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]
):
"""创建指定模式的玩家统计数据""" """创建指定模式的玩家统计数据"""
from app.models.v1_user import PlayerModeStats from app.models.v1_user import PlayerModeStats
# 查找对应模式的统计数据 # 查找对应模式的统计数据
statistics = None statistics = None
for stats in user_statistics: for stats in user_statistics:
if stats.mode == mode: if stats.mode == mode:
statistics = stats statistics = stats
break break
if not statistics: if not statistics:
# 如果没有统计数据,返回默认值 # 如果没有统计数据,返回默认值
return PlayerModeStats( return PlayerModeStats(
@@ -58,9 +59,9 @@ async def _create_player_mode_stats(session: Database, user: User, mode: GameMod
level_progress=0, level_progress=0,
rank=0, rank=0,
country_rank=0, country_rank=0,
history=PlayerStatsHistory() history=PlayerStatsHistory(),
) )
return PlayerModeStats( return PlayerModeStats(
id=user.id, id=user.id,
mode=int(mode), mode=int(mode),
@@ -80,16 +81,16 @@ async def _create_player_mode_stats(session: Database, user: User, mode: GameMod
a_count=statistics.grade_a if statistics.grade_a else 0, a_count=statistics.grade_a if statistics.grade_a else 0,
level=int(statistics.level_current) if statistics.level_current else 1, level=int(statistics.level_current) if statistics.level_current else 1,
level_progress=0, # TODO: 计算等级进度 level_progress=0, # TODO: 计算等级进度
rank=0, # global_rank需要从RankHistory获取 rank=0, # global_rank需要从RankHistory获取
country_rank=0, # country_rank需要从其他地方获取 country_rank=0, # country_rank需要从其他地方获取
history=PlayerStatsHistory() # TODO: 获取PP历史数据 history=PlayerStatsHistory(), # TODO: 获取PP历史数据
) )
async def _create_player_info(user: User): async def _create_player_info(user: User):
"""创建玩家基本信息""" """创建玩家基本信息"""
from app.models.v1_user import PlayerInfo from app.models.v1_user import PlayerInfo
return PlayerInfo( return PlayerInfo(
id=user.id, id=user.id,
name=user.username, name=user.username,
@@ -117,7 +118,7 @@ async def _create_player_info(user: User):
social_twitch=None, social_twitch=None,
social_github=None, social_github=None,
social_osu=None, social_osu=None,
username_history=user.previous_usernames if user.previous_usernames else [] username_history=user.previous_usernames if user.previous_usernames else [],
) )
@@ -139,38 +140,34 @@ async def _count_online_users_optimized(redis):
count = await redis.scard(online_set_key) count = await redis.scard(online_set_key)
logger.debug(f"Using online users set, count: {count}") logger.debug(f"Using online users set, count: {count}")
return count return count
except Exception as e: except Exception as e:
logger.debug(f"Online users set not available: {e}") logger.debug(f"Online users set not available: {e}")
# 方案2: 回退到优化的SCAN操作 # 方案2: 回退到优化的SCAN操作
online_count = 0 online_count = 0
cursor = 0 cursor = 0
scan_iterations = 0 scan_iterations = 0
max_iterations = 50 # 进一步减少最大迭代次数 max_iterations = 50 # 进一步减少最大迭代次数
batch_size = 10000 # 增加批次大小 batch_size = 10000 # 增加批次大小
try: try:
while cursor != 0 or scan_iterations == 0: while cursor != 0 or scan_iterations == 0:
if scan_iterations >= max_iterations: if scan_iterations >= max_iterations:
logger.warning(f"Redis SCAN reached max iterations ({max_iterations}), breaking") logger.warning(f"Redis SCAN reached max iterations ({max_iterations}), breaking")
break break
cursor, keys = await redis.scan( cursor, keys = await redis.scan(cursor, match="metadata:online:*", count=batch_size)
cursor,
match="metadata:online:*",
count=batch_size
)
online_count += len(keys) online_count += len(keys)
scan_iterations += 1 scan_iterations += 1
# 如果连续几次没有找到键,可能已经扫描完成 # 如果连续几次没有找到键,可能已经扫描完成
if len(keys) == 0 and scan_iterations > 2: if len(keys) == 0 and scan_iterations > 2:
break break
logger.debug(f"Found {online_count} online users after {scan_iterations} scan iterations") logger.debug(f"Found {online_count} online users after {scan_iterations} scan iterations")
return online_count return online_count
except Exception as e: except Exception as e:
logger.error(f"Error counting online users: {e}") logger.error(f"Error counting online users: {e}")
# 如果SCAN失败返回0而不是让整个API失败 # 如果SCAN失败返回0而不是让整个API失败
@@ -190,7 +187,7 @@ async def api_get_player_info(
): ):
""" """
获取指定玩家的信息 获取指定玩家的信息
Args: Args:
scope: 信息范围 - stats(统计), events(事件), info(基本信息), all(全部) scope: 信息范围 - stats(统计), events(事件), info(基本信息), all(全部)
id: 用户 ID (可选) id: 用户 ID (可选)
@@ -199,87 +196,67 @@ async def api_get_player_info(
# 验证参数 # 验证参数
if not id and not name: if not id and not name:
raise HTTPException(400, "Must provide either id or name") raise HTTPException(400, "Must provide either id or name")
# 查询用户 # 查询用户
if id: if id:
user = await session.get(User, id) user = await session.get(User, id)
else: else:
user = (await session.exec(select(User).where(User.username == name))).first() user = (await session.exec(select(User).where(User.username == name))).first()
if not user: if not user:
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
return JSONResponse(
status_code=200, return JSONResponse(status_code=200, content={"status": "Player not found."})
content={"status": "Player not found."}
)
try: try:
if scope == "stats": if scope == "stats":
# 获取所有模式的统计数据 # 获取所有模式的统计数据
user_statistics = list(( user_statistics = list(
await session.exec( (await session.exec(select(UserStatistics).where(UserStatistics.user_id == user.id))).all()
select(UserStatistics).where(UserStatistics.user_id == user.id) )
)
).all())
stats_dict = {} stats_dict = {}
# 获取所有游戏模式的统计数据 # 获取所有游戏模式的统计数据
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, GameMode.OSURX, GameMode.OSUAP]
GameMode.OSURX, GameMode.OSUAP]
for mode in all_modes: for mode in all_modes:
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics) mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
stats_dict[str(int(mode))] = mode_stats stats_dict[str(int(mode))] = mode_stats
return GetPlayerInfoResponse( return GetPlayerInfoResponse(player=PlayerStatsResponse(stats=stats_dict))
player=PlayerStatsResponse(stats=stats_dict)
)
elif scope == "events": elif scope == "events":
# 获取事件数据 # 获取事件数据
events = await _get_player_events(session, user.id) events = await _get_player_events(session, user.id)
return GetPlayerInfoResponse( return GetPlayerInfoResponse(player=PlayerEventsResponse(events=events))
player=PlayerEventsResponse(events=events)
)
elif scope == "info": elif scope == "info":
# 获取基本信息 # 获取基本信息
info = await _create_player_info(user) info = await _create_player_info(user)
return GetPlayerInfoResponse( return GetPlayerInfoResponse(player=PlayerInfoResponse(info=info))
player=PlayerInfoResponse(info=info)
)
elif scope == "all": elif scope == "all":
# 获取所有信息 # 获取所有信息
# 统计数据 # 统计数据
user_statistics = list(( user_statistics = list(
await session.exec( (await session.exec(select(UserStatistics).where(UserStatistics.user_id == user.id))).all()
select(UserStatistics).where(UserStatistics.user_id == user.id) )
)
).all())
stats_dict = {} stats_dict = {}
all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, all_modes = [GameMode.OSU, GameMode.TAIKO, GameMode.FRUITS, GameMode.MANIA, GameMode.OSURX, GameMode.OSUAP]
GameMode.OSURX, GameMode.OSUAP]
for mode in all_modes: for mode in all_modes:
mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics) mode_stats = await _create_player_mode_stats(session, user, mode, user_statistics)
stats_dict[str(int(mode))] = mode_stats stats_dict[str(int(mode))] = mode_stats
# 基本信息 # 基本信息
info = await _create_player_info(user) info = await _create_player_info(user)
# 事件 # 事件
events = await _get_player_events(session, user.id) events = await _get_player_events(session, user.id)
return GetPlayerInfoResponse( return GetPlayerInfoResponse(player=PlayerAllResponse(info=info, stats=stats_dict, events=events))
player=PlayerAllResponse(
info=info,
stats=stats_dict,
events=events
)
)
except Exception as e: except Exception as e:
logger.error(f"Error processing get_player_info for user {user.id}: {e}") logger.error(f"Error processing get_player_info for user {user.id}: {e}")
raise HTTPException(500, "Internal server error") raise HTTPException(500, "Internal server error")
@@ -296,50 +273,49 @@ async def api_get_player_count(
): ):
""" """
获取玩家数量统计 获取玩家数量统计
Returns: Returns:
包含在线用户数和总用户数的响应 包含在线用户数和总用户数的响应
""" """
try: try:
redis = get_redis() redis = get_redis()
online_cache_key = "stats:online_users_count" online_cache_key = "stats:online_users_count"
cached_online = await redis.get(online_cache_key) cached_online = await redis.get(online_cache_key)
if cached_online is not None: if cached_online is not None:
online_count = int(cached_online) online_count = int(cached_online)
logger.debug(f"Using cached online user count: {online_count}") logger.debug(f"Using cached online user count: {online_count}")
else: else:
logger.debug("Cache miss, scanning Redis for online users") logger.debug("Cache miss, scanning Redis for online users")
online_count = await _count_online_users_optimized(redis) online_count = await _count_online_users_optimized(redis)
await redis.setex(online_cache_key, 30, str(online_count)) await redis.setex(online_cache_key, 30, str(online_count))
logger.debug(f"Cached online user count: {online_count} for 30 seconds") logger.debug(f"Cached online user count: {online_count} for 30 seconds")
cache_key = "stats:total_users" cache_key = "stats:total_users"
cached_total = await redis.get(cache_key) cached_total = await redis.get(cache_key)
if cached_total is not None: if cached_total is not None:
total_count = int(cached_total) total_count = int(cached_total)
logger.debug(f"Using cached total user count: {total_count}") logger.debug(f"Using cached total user count: {total_count}")
else: else:
logger.debug("Cache miss, querying database for total user count") logger.debug("Cache miss, querying database for total user count")
from sqlmodel import func, select from sqlmodel import func, select
total_count_result = await session.exec(
select(func.count()).select_from(User) total_count_result = await session.exec(select(func.count()).select_from(User))
)
total_count = total_count_result.one() total_count = total_count_result.one()
await redis.setex(cache_key, 3600, str(total_count)) await redis.setex(cache_key, 3600, str(total_count))
logger.debug(f"Cached total user count: {total_count} for 1 hour") logger.debug(f"Cached total user count: {total_count} for 1 hour")
return GetPlayerCountResponse( return GetPlayerCountResponse(
counts=PlayerCountData( counts=PlayerCountData(
online=online_count, online=online_count,
total=max(0, total_count - 1) # 减去1个机器人账户确保不为负数 total=max(0, total_count - 1), # 减去1个机器人账户确保不为负数
) )
) )
except Exception as e: except Exception as e:
logger.error(f"Error getting player count: {e}") logger.error(f"Error getting player count: {e}")
raise HTTPException(500, "Internal server error") raise HTTPException(500, "Internal server error")

View File

@@ -3,31 +3,23 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import Literal from typing import Literal
from app.database.events import Event, EventType
from app.database.lazer_user import User from app.database.lazer_user import User
from app.database.rank_history import RankHistory, RankHistoryResp
from app.database.statistics import UserStatistics, UserStatisticsResp from app.database.statistics import UserStatistics, UserStatisticsResp
from app.dependencies.database import Database, get_redis from app.dependencies.database import Database, get_redis
from app.log import logger from app.log import logger
from app.models.score import GameMode from app.models.score import GameMode
from app.models.v1_user import ( from app.models.v1_user import (
GetPlayerInfoResponse,
PlayerAllResponse,
PlayerEventItem, PlayerEventItem,
PlayerEventsResponse,
PlayerInfo, PlayerInfo,
PlayerInfoResponse,
PlayerModeStats, PlayerModeStats,
PlayerStatsHistory, PlayerStatsHistory,
PlayerStatsResponse,
) )
from app.service.user_cache_service import get_user_cache_service from app.service.user_cache_service import get_user_cache_service
from .router import AllStrModel, router from .router import AllStrModel, router
from fastapi import BackgroundTasks, HTTPException, Query from fastapi import BackgroundTasks, HTTPException, Query
from pydantic import BaseModel, Field from sqlmodel import select
from sqlmodel import col, select
class V1User(AllStrModel): class V1User(AllStrModel):
@@ -171,19 +163,17 @@ async def get_user(
# 以下为 get_player_info 接口相关的实现函数 # 以下为 get_player_info 接口相关的实现函数
async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMode, days: int = 30) -> list[float]: async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMode, days: int = 30) -> list[float]:
"""获取指定模式的 PP 历史数据""" """获取指定模式的 PP 历史数据"""
try: try:
# 获取最近 30 天的排名历史(由于没有 PP 历史,我们使用当前的 PP 填充) # 获取最近 30 天的排名历史(由于没有 PP 历史,我们使用当前的 PP 填充)
stats = ( stats = (
await session.exec( await session.exec(
select(UserStatistics).where( select(UserStatistics).where(UserStatistics.user_id == user_id, UserStatistics.mode == mode)
UserStatistics.user_id == user_id,
UserStatistics.mode == mode
)
) )
).first() ).first()
current_pp = stats.pp if stats else 0.0 current_pp = stats.pp if stats else 0.0
# 创建 30 天的 PP 历史(使用当前 PP 值填充) # 创建 30 天的 PP 历史(使用当前 PP 值填充)
return [current_pp] * days return [current_pp] * days
@@ -193,10 +183,7 @@ async def _get_pp_history_for_mode(session: Database, user_id: int, mode: GameMo
async def _create_player_mode_stats( async def _create_player_mode_stats(
session: Database, session: Database, user: User, mode: GameMode, user_statistics: list[UserStatistics]
user: User,
mode: GameMode,
user_statistics: list[UserStatistics]
) -> PlayerModeStats: ) -> PlayerModeStats:
"""创建单个模式的玩家统计数据""" """创建单个模式的玩家统计数据"""
# 查找对应模式的统计数据 # 查找对应模式的统计数据
@@ -205,7 +192,7 @@ async def _create_player_mode_stats(
if stat.mode == mode: if stat.mode == mode:
stats = stat stats = stat
break break
if not stats: if not stats:
# 如果没有统计数据,创建默认数据 # 如果没有统计数据,创建默认数据
pp_history = [0.0] * 30 pp_history = [0.0] * 30
@@ -230,26 +217,27 @@ async def _create_player_mode_stats(
level_progress=0, level_progress=0,
rank=0, rank=0,
country_rank=0, country_rank=0,
history=PlayerStatsHistory(pp=pp_history) history=PlayerStatsHistory(pp=pp_history),
) )
# 获取排名信息 # 获取排名信息
try: try:
from app.database.statistics import get_rank from app.database.statistics import get_rank
global_rank = await get_rank(session, stats) or 0 global_rank = await get_rank(session, stats) or 0
country_rank = await get_rank(session, stats, user.country_code) or 0 country_rank = await get_rank(session, stats, user.country_code) or 0
except Exception as e: except Exception as e:
logger.error(f"Error getting rank for user {user.id}: {e}") logger.error(f"Error getting rank for user {user.id}: {e}")
global_rank = 0 global_rank = 0
country_rank = 0 country_rank = 0
# 获取 PP 历史 # 获取 PP 历史
pp_history = await _get_pp_history_for_mode(session, user.id, mode) pp_history = await _get_pp_history_for_mode(session, user.id, mode)
# 计算等级进度 # 计算等级进度
level_current = int(stats.level_current) level_current = int(stats.level_current)
level_progress = int((stats.level_current - level_current) * 100) level_progress = int((stats.level_current - level_current) * 100)
return PlayerModeStats( return PlayerModeStats(
id=user.id, id=user.id,
mode=int(mode), mode=int(mode),
@@ -271,7 +259,7 @@ async def _create_player_mode_stats(
level_progress=level_progress, level_progress=level_progress,
rank=global_rank, rank=global_rank,
country_rank=country_rank, country_rank=country_rank,
history=PlayerStatsHistory(pp=pp_history) history=PlayerStatsHistory(pp=pp_history),
) )
@@ -310,10 +298,10 @@ async def _create_player_info(user: User) -> PlayerInfo:
userpage_content=user.page.get("html", "") if user.page else "", userpage_content=user.page.get("html", "") if user.page else "",
recentFailed=0, recentFailed=0,
social_discord=user.discord, social_discord=user.discord,
social_youtube=None, social_youtube=None,
social_twitter=user.twitter, social_twitter=user.twitter,
social_twitch=None, social_twitch=None,
social_github=None, social_github=None,
social_osu=None, social_osu=None,
username_history=user.previous_usernames or [] username_history=user.previous_usernames or [],
) )

View File

@@ -19,8 +19,8 @@ from app.router import (
private_router, private_router,
redirect_api_router, redirect_api_router,
) )
from app.router.v1 import api_v1_public_router
from app.router.redirect import redirect_router from app.router.redirect import redirect_router
from app.router.v1 import api_v1_public_router
from app.scheduler.cache_scheduler import start_cache_scheduler, stop_cache_scheduler from app.scheduler.cache_scheduler import start_cache_scheduler, stop_cache_scheduler
from app.scheduler.database_cleanup_scheduler import ( from app.scheduler.database_cleanup_scheduler import (
start_database_cleanup_scheduler, start_database_cleanup_scheduler,