File tree Expand file tree Collapse file tree 10 files changed +340
-5
lines changed
{% if add_worker %}worker{% endif %} Expand file tree Collapse file tree 10 files changed +340
-5
lines changed Original file line number Diff line number Diff line change 11# YAML objects named with dot is anchors, which are not recognized as jobs
22.run_bot: &run_bot
33 - docker rm -f ${CONTAINER_NAME} || true
4+ {% if add_worker -%} - docker rm -f ${CONTAINER_NAME}-worker || true{% - endif %}
45 - docker pull ${CONTAINER_RELEASE_IMAGE}
56 # Add new envs here. Don't forget to add them in example.env and docker-compose files.
67 - docker run
1718 -e BOT_CREDENTIALS="${BOT_CREDENTIALS}"
1819 -e DEBUG="${DEBUG:-false}"
1920 $CONTAINER_RELEASE_IMAGE
21+ {% if add_worker -%}
22+ # Add envs for worker here
23+ - docker run
24+ -d
25+ --name ${CONTAINER_NAME}-worker
26+ --restart always
27+ --log-opt max-size=10m
28+ --log-opt max-file=5
29+ -e POSTGRES_DSN="${POSTGRES_DSN}"
30+ -e REDIS_DSN="${REDIS_DSN}"
31+ -e BOT_CREDENTIALS="${BOT_CREDENTIALS}"
32+ -e DEBUG="${DEBUG:-false}"
33+ ${CONTAINER_RELEASE_IMAGE}
34+ bash -c 'PYTHONPATH="$PYTHONPATH:$PWD" saq app.worker.worker.settings'
35+ {% - endif %}
2036
2137.create_db: &create_db
2238 - psql -c "create user \"${POSTGRES_USER}\"" postgres || true
@@ -147,6 +163,9 @@ deploy.botstest.stop:
147163 extends: deploy.botstest
148164 script:
149165 - docker rm -f ${CONTAINER_NAME} || true
166+ {% if add_worker -%}
167+ - docker rm -f ${CONTAINER_NAME} ${CONTAINER_NAME}-worker || true
168+ {% - endif %}
150169 - psql -c "select pg_terminate_backend(pid) from pg_stat_activity \
151170 where datname = '${POSTGRES_DB}';" postgres || true
152171 - psql -c "drop database \"${POSTGRES_DB}\"" postgres || true
Original file line number Diff line number Diff line change 11"""Bot dependency for healthcheck."""
22
3+ {% if add_worker -%}
4+ from asyncio.exceptions import TimeoutError
5+ {% - endif %}
36from typing import Optional
47
58from fastapi import Depends, Request
69from pybotx import Bot
710from sqlalchemy.sql import text
811
12+ {% if add_worker -%}
13+ from app.settings import settings
14+ from app.worker.worker import queue
15+ {% - endif %}
16+
917
1018async def check_db_connection(request: Request) -> Optional[str]:
1119 assert isinstance(request.app.state.bot, Bot)
@@ -33,3 +41,24 @@ async def check_redis_connection(request: Request) -> Optional[str]:
3341
3442
3543check_redis_connection_dependency = Depends(check_redis_connection)
44+ {% - if add_worker %}
45+
46+
47+ async def check_worker_status() -> Optional[str]:
48+ job = await queue.enqueue("healthcheck")
49+
50+ if not job:
51+ return None
52+
53+ try:
54+ await job.refresh(settings.WORKER_TIMEOUT_SEC)
55+ except TimeoutError:
56+ return "Worker is overloaded or not launched"
57+ except Exception as exc:
58+ return str(exc)
59+
60+ return None
61+
62+
63+ check_worker_status_dependency = Depends(check_worker_status)
64+ {% - endif %}
Original file line number Diff line number Diff line change 77from app.api.dependencies.healthcheck import (
88 check_db_connection_dependency,
99 check_redis_connection_dependency,
10+ {% if add_worker -%}
11+ check_worker_status_dependency,
12+ {% - endif %}
1013)
1114from app.services.healthcheck import (
1215 HealthCheckResponse,
2124async def healthcheck(
2225 redis_connection_error: Optional[str] = check_redis_connection_dependency,
2326 db_connection_error: Optional[str] = check_db_connection_dependency,
27+ {% if add_worker -%}
28+ worker_status_error: Optional[str] = check_worker_status_dependency,
29+ {% - endif %}
2430) -> HealthCheckResponse:
2531 """Check the health of the bot and services."""
2632 healthcheck_builder = HealthCheckResponseBuilder()
@@ -30,5 +36,10 @@ async def healthcheck(
3036 healthcheck_builder.add_healthcheck_result(
3137 HealthCheckServiceResult(name="redis", error=redis_connection_error)
3238 )
39+ {% if add_worker -%}
40+ healthcheck_builder.add_healthcheck_result(
41+ HealthCheckServiceResult(name="worker", error=worker_status_error)
42+ )
43+ {% - endif %}
3344
3445 return healthcheck_builder.build()
Original file line number Diff line number Diff line change @@ -30,6 +30,11 @@ class Config: # noqa: WPS431
3030 # redis
3131 REDIS_DSN: str
3232
33+ {% if add_worker -%}
34+ # healthcheck
35+ WORKER_TIMEOUT_SEC: float = 4
36+ {% - endif %}
37+
3338 @validator("BOT_CREDENTIALS", pre=True)
3439 @classmethod
3540 def parse_bot_credentials(cls, raw_credentials: Any) -> List[BotAccountWithSecret]:
Original file line number Diff line number Diff line change 1+ """Tasks worker configuration."""
2+
3+ from typing import Any, Dict, Literal
4+
5+ from pybotx import Bot
6+ from redis import asyncio as aioredis
7+ from saq import Queue
8+
9+ from app.caching.callback_redis_repo import CallbackRedisRepo
10+ from app.logger import logger
11+
12+ # `saq` import its own settings and hides our module
13+ from app.settings import settings as app_settings
14+
15+ SaqCtx = Dict[str, Any]
16+
17+
18+ async def startup(ctx: SaqCtx) -> None:
19+ from app.bot.bot import get_bot # noqa: WPS433
20+
21+ callback_repo = CallbackRedisRepo(aioredis.from_url(app_settings.REDIS_DSN))
22+ bot = get_bot(callback_repo)
23+
24+ await bot.startup(fetch_tokens=False)
25+
26+ ctx["bot"] = bot
27+
28+ logger.info("Worker started")
29+
30+
31+ async def shutdown(ctx: SaqCtx) -> None:
32+ bot: Bot = ctx["bot"]
33+ await bot.shutdown()
34+
35+ logger.info("Worker stopped")
36+
37+
38+ async def healthcheck(_: SaqCtx) -> Literal[True]:
39+ return True
40+
41+
42+ queue = Queue(aioredis.from_url(app_settings.REDIS_DSN), name="{{bot_project_name}}")
43+
44+ settings = {
45+ "queue": queue,
46+ "functions": [healthcheck],
47+ "cron_jobs": [],
48+ "concurrency": 8,
49+ "startup": startup,
50+ "shutdown": shutdown,
51+ }
Original file line number Diff line number Diff line change @@ -11,6 +11,11 @@ bot_description:
1111 help : Description for README.md. First line will be added pyproject.toml
1212 default : TODO
1313
14+ add_worker :
15+ help : Include tasks worker in `docker-compose.yml`
16+ type : bool
17+ default : yes
18+
1419
1520bot_name_underscored :
1621 default : " {{bot_project_name|replace('-', '_')}}"
Original file line number Diff line number Diff line change @@ -4,28 +4,41 @@ services:
44 {{bot_project_name}}:
55 build: .
66 container_name: {{bot_project_name}}
7- environment:
7+ environment: &environment
88 - BOT_CREDENTIALS=cts_host@secret_key@bot_id
99 - POSTGRES_DSN=postgres://postgres:postgres@{{bot_project_name}}-postgres/{{bot_name_underscored}}_db
1010 - REDIS_DSN=redis://{{bot_project_name}}-redis/0
1111 - DEBUG=true
1212 ports:
1313 - "8000:8000" # Отредактируйте порт хоста (первый), если он уже занят
1414 restart: always
15- depends_on:
15+ depends_on: &depends_on
1616 - postgres
1717 - redis
18- logging:
18+ logging: &logging
1919 driver: "json-file"
2020 options:
2121 max-size: "10m"
2222 max-file: "10"
23- ulimits:
23+ ulimits: &ulimits
2424 nproc: 65535
2525 nofile:
2626 soft: 20000
2727 hard: 40000
2828
29+ {% if add_worker -%}
30+ {{bot_project_name}}-worker:
31+ build: .
32+ container_name: {{bot_project_name}}-worker
33+ # '$$' prevents docker-compose from interpolating a value
34+ command: /bin/sh -c 'PYTHONPATH="$$PYTHONPATH:$$PWD" saq app.worker.worker.settings'
35+ environment: *environment
36+ restart: always
37+ depends_on: *depends_on
38+ logging: *logging
39+ ulimits: *ulimits
40+
41+ {% endif -%}
2942 postgres:
3043 image: postgres:15.3-alpine
3144 container_name: {{bot_project_name}}-postgres
You can’t perform that action at this time.
0 commit comments