feat: implement async scheduler with lifespan management and update README

This commit is contained in:
grillazz
2026-10-22 13:29:43 +02:00
parent 4ea1ef0fe5
commit d1661f6036
6 changed files with 106 additions and 44 deletions
+22 -26
View File
@@ -1,13 +1,16 @@
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from datetime import datetime
from apscheduler import AsyncScheduler
from apscheduler import AsyncScheduler, ConflictPolicy
from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
from apscheduler.eventbrokers.redis import RedisEventBroker
from apscheduler.triggers.interval import IntervalTrigger
from attrs import define
from rotoger import get_logger
from sqlalchemy import text
from starlette.types import ASGIApp, Receive, Scope, Send
from app.database import AsyncSessionFactory
from app.config import settings as global_settings
from app.database import AsyncSessionFactory, engine
logger = get_logger()
@@ -21,26 +24,19 @@ async def tick():
return True
@define
class SchedulerMiddleware:
app: ASGIApp
scheduler: AsyncScheduler
@asynccontextmanager
async def scheduler_lifespan() -> AsyncIterator[None]:
scheduler = AsyncScheduler(
SQLAlchemyDataStore(engine, schema="scheduler"),
RedisEventBroker(client_or_url=global_settings.redis_url.unicode_string()),
)
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
"""
Handles the incoming request and schedules tasks if the scope type is 'lifespan'.
Args:
scope (Scope): The ASGI scope dictionary containing request information.
receive (Receive): The ASGI receive callable.
send (Send): The ASGI send callable.
"""
if scope["type"] == "lifespan":
async with self.scheduler:
await self.scheduler.add_schedule(
tick, IntervalTrigger(seconds=25), id="tick-sql-25"
)
await self.scheduler.start_in_background()
await self.app(scope, receive, send)
else:
await self.app(scope, receive, send)
async with scheduler:
await scheduler.add_schedule(
tick,
IntervalTrigger(seconds=25),
id="tick-sql-25",
conflict_policy=ConflictPolicy.replace,
)
await scheduler.start_in_background()
yield