Skip to content

Commit c93e253

Browse files
author
Ilyas Gasanov
committed
[DOP-21799] Refactor celery initialization
1 parent a093ada commit c93e253

File tree

11 files changed

+86
-63
lines changed

11 files changed

+86
-63
lines changed

syncmaster/backend/__init__.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-FileCopyrightText: 2023-2024 MTS PJSC
22
# SPDX-License-Identifier: Apache-2.0
3+
from celery import Celery
34
from fastapi import FastAPI, HTTPException
45
from fastapi.exceptions import RequestValidationError
56
from pydantic import ValidationError
@@ -20,6 +21,15 @@
2021
from syncmaster.exceptions import SyncmasterError
2122

2223

24+
def celery_factory(settings: Settings) -> Celery:
25+
app = Celery(
26+
__name__,
27+
broker=settings.broker.url,
28+
backend="db+" + settings.database.sync_url,
29+
)
30+
return app
31+
32+
2333
def application_factory(settings: Settings) -> FastAPI:
2434
application = FastAPI(
2535
title="Syncmaster",
@@ -30,6 +40,7 @@ def application_factory(settings: Settings) -> FastAPI:
3040
redoc_url=None,
3141
)
3242
application.state.settings = settings
43+
application.state.celery = celery_factory(settings)
3344
application.include_router(api_router)
3445
application.exception_handler(RequestValidationError)(validation_exception_handler)
3546
application.exception_handler(ValidationError)(validation_exception_handler)
@@ -44,6 +55,7 @@ def application_factory(settings: Settings) -> FastAPI:
4455
{
4556
Settings: lambda: settings,
4657
UnitOfWork: get_uow(session_factory, settings=settings),
58+
Celery: lambda: application.state.celery,
4759
},
4860
)
4961

syncmaster/backend/api/v1/runs.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
from typing import Annotated
66

77
from asgi_correlation_id import correlation_id
8+
from celery import Celery
89
from fastapi import APIRouter, Depends, Query
910
from jinja2 import Template
1011
from kombu.exceptions import KombuError
1112

12-
from syncmaster.backend.celery import app as celery
1313
from syncmaster.backend.dependencies import Stub
1414
from syncmaster.backend.services import UnitOfWork, get_user
1515
from syncmaster.backend.settings import ServerAppSettings as Settings
@@ -84,6 +84,7 @@ async def read_run(
8484
async def start_run(
8585
create_run_data: CreateRunSchema,
8686
settings: Annotated[Settings, Depends(Stub(Settings))],
87+
celery: Annotated[Celery, Depends(Stub(Celery))],
8788
unit_of_work: UnitOfWork = Depends(UnitOfWork),
8889
current_user: User = Depends(get_user(is_active=True)),
8990
) -> ReadRunSchema:

syncmaster/backend/celery.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

syncmaster/scheduler/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
11
# SPDX-FileCopyrightText: 2023-2024 MTS PJSC
22
# SPDX-License-Identifier: Apache-2.0
3-
from syncmaster.scheduler.transfer_fetcher import TransferFetcher
4-
from syncmaster.scheduler.transfer_job_manager import TransferJobManager
3+
from celery import Celery
4+
5+
from syncmaster.scheduler.settings import SchedulerAppSettings
6+
7+
8+
def celery_factory(settings: SchedulerAppSettings) -> Celery:
9+
app = Celery(
10+
__name__,
11+
broker=settings.broker.url,
12+
backend="db+" + settings.database.sync_url,
13+
)
14+
return app

syncmaster/scheduler/celery.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# SPDX-FileCopyrightText: 2023-2024 MTS PJSC
22
# SPDX-License-Identifier: Apache-2.0
3+
from syncmaster.scheduler import celery_factory
34
from syncmaster.scheduler.settings import SchedulerAppSettings
4-
from syncmaster.worker import celery_factory
55

6+
# Global object, since the TransferJobManager.send_job_to_celery method is static
67
app = celery_factory(SchedulerAppSettings())

syncmaster/scheduler/transfer_job_manager.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,11 @@ def update_jobs(self, transfers: list[Transfer]) -> None:
5050
@staticmethod
5151
async def send_job_to_celery(transfer_id: int) -> None:
5252
"""
53-
Do not pass additional arguments like settings,
53+
1. Do not pass additional arguments like settings,
5454
otherwise they will be serialized in jobs table.
55+
2. Instance methods are bound to specific objects and cannot be reliably serialized
56+
due to the weak reference problem. Use a static method instead, as it is not
57+
object-specific and can be serialized.
5558
"""
5659
settings = Settings()
5760

syncmaster/worker/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
from celery import Celery
44

55
from syncmaster.worker.base import WorkerTask
6+
from syncmaster.worker.settings import WorkerAppSettings
67

78

8-
def celery_factory(settings) -> Celery:
9+
def celery_factory(settings: WorkerAppSettings) -> Celery:
910
app = Celery(
1011
__name__,
1112
broker=settings.broker.url,

tests/conftest.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import time
55
from collections.abc import AsyncGenerator, Callable
66
from pathlib import Path
7+
from unittest.mock import AsyncMock, Mock
78

89
import pytest
910
import pytest_asyncio
1011
from alembic.config import Config as AlembicConfig
1112
from celery import Celery
13+
from fastapi import FastAPI
1214
from httpx import AsyncClient
1315
from sqlalchemy.ext.asyncio import (
1416
AsyncEngine,
@@ -129,6 +131,26 @@ async def session(sessionmaker: async_sessionmaker[AsyncSession]):
129131
await session.close()
130132

131133

134+
@pytest.fixture(scope="session")
135+
def mocked_celery() -> Celery:
136+
celery_app = Mock(Celery)
137+
celery_app.send_task = AsyncMock()
138+
return celery_app
139+
140+
141+
@pytest_asyncio.fixture(scope="session")
142+
async def app(settings: Settings, mocked_celery: Celery) -> FastAPI:
143+
app = application_factory(settings=settings)
144+
app.dependency_overrides[Celery] = lambda: mocked_celery
145+
return app
146+
147+
148+
@pytest_asyncio.fixture(scope="session")
149+
async def client_with_mocked_celery(app: FastAPI) -> AsyncGenerator:
150+
async with AsyncClient(app=app, base_url="http://testserver") as client:
151+
yield client
152+
153+
132154
@pytest_asyncio.fixture(scope="session")
133155
async def client(settings: Settings) -> AsyncGenerator:
134156
logger.info("START CLIENT FIXTURE")
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
from syncmaster.scheduler.celery import app as celery
1+
from syncmaster.worker.celery import app as celery
22

33
celery.conf.update(imports=list(celery.conf.imports) + ["tests.test_integration.test_scheduler.test_task"])

tests/test_integration/test_scheduler/test_scheduler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
from syncmaster.backend.settings import ServerAppSettings as Settings
99
from syncmaster.db.models import Run, Status
10-
from syncmaster.scheduler import TransferFetcher, TransferJobManager
10+
from syncmaster.scheduler.transfer_fetcher import TransferFetcher
11+
from syncmaster.scheduler.transfer_job_manager import TransferJobManager
1112
from tests.mocks import MockTransfer
1213

1314
pytestmark = [pytest.mark.asyncio, pytest.mark.worker, pytest.mark.scheduler_integration]

0 commit comments

Comments
 (0)