Skip to content

Commit a84876c

Browse files
Merge branch 'master' into is8102/add-search-api-in-storage
2 parents a00bdfa + be7e1e2 commit a84876c

File tree

113 files changed

+2784
-1168
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

113 files changed

+2784
-1168
lines changed

.env-devel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ LICENSES_ITIS_VIP_API_URL=https://replace-with-itis-api/{category}
147147
LICENSES_ITIS_VIP_CATEGORIES='{"HumanWholeBody": "Humans", "HumanBodyRegion": "Humans (Region)", "AnimalWholeBody": "Animal"}'
148148
LICENSES_SPEAG_PHANTOMS_API_URL=https://replace-with-speag-api/{category}
149149
LICENSES_SPEAG_PHANTOMS_CATEGORIES='{"ComputationalPhantom": "Phantom of the Opera"}'
150+
LONG_RUNNING_TASKS_NAMESPACE_SUFFIX=development
150151

151152
# Can use 'docker run -it itisfoundation/invitations:latest simcore-service-invitations generate-dotenv --auto-password'
152153
INVITATIONS_DEFAULT_PRODUCT=osparc

.github/workflows/ci-testing-deploy.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,7 @@ jobs:
12521252
unit-test-service-library:
12531253
needs: changes
12541254
if: ${{ needs.changes.outputs.service-library == 'true' || github.event_name == 'push' || github.event.inputs.force_all_builds == 'true' }}
1255-
timeout-minutes: 18 # if this timeout gets too small, then split the tests
1255+
timeout-minutes: 20 # if this timeout gets too small, then split the tests
12561256
name: "[unit] service-library"
12571257
runs-on: ${{ matrix.os }}
12581258
strategy:

api/specs/web-server/_long_running_tasks.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,44 +32,40 @@
3232
@router.get(
3333
"/tasks",
3434
response_model=Envelope[list[TaskGet]],
35-
name="list_tasks",
36-
description="Lists all long running tasks",
3735
responses=_export_data_responses,
3836
)
39-
def get_async_jobs(): ...
37+
def get_async_jobs():
38+
"""Lists all long running tasks"""
4039

4140

4241
@router.get(
4342
"/tasks/{task_id}",
4443
response_model=Envelope[TaskStatus],
45-
name="get_task_status",
46-
description="Retrieves the status of a task",
4744
responses=_export_data_responses,
4845
)
4946
def get_async_job_status(
5047
_path_params: Annotated[_PathParam, Depends()],
51-
): ...
48+
):
49+
"""Retrieves the status of a task"""
5250

5351

5452
@router.delete(
5553
"/tasks/{task_id}",
56-
name="cancel_and_delete_task",
57-
description="Cancels and deletes a task",
5854
responses=_export_data_responses,
5955
status_code=status.HTTP_204_NO_CONTENT,
6056
)
6157
def cancel_async_job(
6258
_path_params: Annotated[_PathParam, Depends()],
63-
): ...
59+
):
60+
"""Cancels and removes a task"""
6461

6562

6663
@router.get(
6764
"/tasks/{task_id}/result",
6865
response_model=Any,
69-
name="get_task_result",
70-
description="Retrieves the result of a task",
7166
responses=_export_data_responses,
7267
)
7368
def get_async_job_result(
7469
_path_params: Annotated[_PathParam, Depends()],
75-
): ...
70+
):
71+
"""Retrieves the result of a task"""

api/specs/web-server/_long_running_tasks_legacy.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,11 @@ async def get_task_status(
4242

4343
@router.delete(
4444
"/{task_id}",
45-
name="cancel_and_delete_task",
46-
description="Cancels and deletes a task",
45+
name="remove_task",
46+
description="Cancels and removes a task",
4747
status_code=status.HTTP_204_NO_CONTENT,
4848
)
49-
async def cancel_and_delete_task(
49+
async def remove_task(
5050
_path_params: Annotated[_PathParam, Depends()],
5151
): ...
5252

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
from datetime import timedelta
2+
3+
import pytest
4+
from pytest_mock import MockerFixture
5+
6+
7+
@pytest.fixture
8+
async def fast_long_running_tasks_cancellation(
9+
mocker: MockerFixture,
10+
) -> None:
11+
mocker.patch(
12+
"servicelib.long_running_tasks.task._CANCEL_TASKS_CHECK_INTERVAL",
13+
new=timedelta(seconds=1),
14+
)

packages/service-library/src/servicelib/aiohttp/long_running_tasks/_manager.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,12 @@
1-
import datetime
2-
31
from aiohttp import web
4-
from settings_library.redis import RedisSettings
52

63
from ...long_running_tasks.base_long_running_manager import BaseLongRunningManager
74
from ...long_running_tasks.models import TaskContext
8-
from ...long_running_tasks.task import RedisNamespace, TasksManager
95
from ._constants import APP_LONG_RUNNING_MANAGER_KEY
106
from ._request import get_task_context
117

128

139
class AiohttpLongRunningManager(BaseLongRunningManager):
14-
def __init__(
15-
self,
16-
app: web.Application,
17-
stale_task_check_interval: datetime.timedelta,
18-
stale_task_detect_timeout: datetime.timedelta,
19-
redis_settings: RedisSettings,
20-
redis_namespace: RedisNamespace,
21-
):
22-
self._app = app
23-
self._tasks_manager = TasksManager(
24-
stale_task_check_interval=stale_task_check_interval,
25-
stale_task_detect_timeout=stale_task_detect_timeout,
26-
redis_settings=redis_settings,
27-
redis_namespace=redis_namespace,
28-
)
29-
30-
@property
31-
def tasks_manager(self) -> TasksManager:
32-
return self._tasks_manager
33-
34-
async def setup(self) -> None:
35-
await self._tasks_manager.setup()
36-
37-
async def teardown(self) -> None:
38-
await self._tasks_manager.teardown()
3910

4011
@staticmethod
4112
def get_task_context(request: web.Request) -> TaskContext:

packages/service-library/src/servicelib/aiohttp/long_running_tasks/_routes.py

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
1-
from typing import Any
1+
from typing import Annotated, Any
22

33
from aiohttp import web
4-
from pydantic import BaseModel
4+
from models_library.rest_base import RequestParameters
5+
from pydantic import BaseModel, Field
56

67
from ...aiohttp import status
78
from ...long_running_tasks import lrt_api
89
from ...long_running_tasks.models import TaskGet, TaskId
9-
from ..requests_validation import parse_request_path_parameters_as
10+
from ..requests_validation import (
11+
parse_request_path_parameters_as,
12+
parse_request_query_parameters_as,
13+
)
1014
from ..rest_responses import create_data_response
1115
from ._manager import get_long_running_manager
1216

@@ -26,10 +30,11 @@ async def list_tasks(request: web.Request) -> web.Response:
2630
task_id=t.task_id,
2731
status_href=f"{request.app.router['get_task_status'].url_for(task_id=t.task_id)}",
2832
result_href=f"{request.app.router['get_task_result'].url_for(task_id=t.task_id)}",
29-
abort_href=f"{request.app.router['cancel_and_delete_task'].url_for(task_id=t.task_id)}",
33+
abort_href=f"{request.app.router['remove_task'].url_for(task_id=t.task_id)}",
3034
)
3135
for t in await lrt_api.list_tasks(
32-
long_running_manager.tasks_manager,
36+
long_running_manager.rpc_client,
37+
long_running_manager.lrt_namespace,
3338
long_running_manager.get_task_context(request),
3439
)
3540
]
@@ -42,7 +47,8 @@ async def get_task_status(request: web.Request) -> web.Response:
4247
long_running_manager = get_long_running_manager(request.app)
4348

4449
task_status = await lrt_api.get_task_status(
45-
long_running_manager.tasks_manager,
50+
long_running_manager.rpc_client,
51+
long_running_manager.lrt_namespace,
4652
long_running_manager.get_task_context(request),
4753
path_params.task_id,
4854
)
@@ -56,20 +62,36 @@ async def get_task_result(request: web.Request) -> web.Response | Any:
5662

5763
# NOTE: this might raise an exception that will be catched by the _error_handlers
5864
return await lrt_api.get_task_result(
59-
long_running_manager.tasks_manager,
65+
long_running_manager.rpc_client,
66+
long_running_manager.lrt_namespace,
6067
long_running_manager.get_task_context(request),
6168
path_params.task_id,
6269
)
6370

6471

65-
@routes.delete("/{task_id}", name="cancel_and_delete_task")
66-
async def cancel_and_delete_task(request: web.Request) -> web.Response:
72+
class _RemoveTaskQueryParams(RequestParameters):
73+
wait_for_removal: Annotated[
74+
bool,
75+
Field(
76+
description=(
77+
"when True waits for the task to be removed "
78+
"completly instead of returning immediately"
79+
)
80+
),
81+
] = True
82+
83+
84+
@routes.delete("/{task_id}", name="remove_task")
85+
async def remove_task(request: web.Request) -> web.Response:
6786
path_params = parse_request_path_parameters_as(_PathParam, request)
87+
query_params = parse_request_query_parameters_as(_RemoveTaskQueryParams, request)
6888
long_running_manager = get_long_running_manager(request.app)
6989

7090
await lrt_api.remove_task(
71-
long_running_manager.tasks_manager,
91+
long_running_manager.rpc_client,
92+
long_running_manager.lrt_namespace,
7293
long_running_manager.get_task_context(request),
7394
path_params.task_id,
95+
wait_for_removal=query_params.wait_for_removal,
7496
)
7597
return web.json_response(status=status.HTTP_204_NO_CONTENT)

packages/service-library/src/servicelib/aiohttp/long_running_tasks/_server.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,25 @@
88
from aiohttp.web import HTTPException
99
from common_library.json_serialization import json_dumps
1010
from pydantic import AnyHttpUrl, TypeAdapter
11+
from settings_library.rabbit import RabbitSettings
1112
from settings_library.redis import RedisSettings
1213

1314
from ...aiohttp import status
1415
from ...long_running_tasks import lrt_api
15-
from ...long_running_tasks._redis_serialization import (
16+
from ...long_running_tasks._serialization import (
1617
BaseObjectSerializer,
1718
register_custom_serialization,
1819
)
1920
from ...long_running_tasks.constants import (
2021
DEFAULT_STALE_TASK_CHECK_INTERVAL,
2122
DEFAULT_STALE_TASK_DETECT_TIMEOUT,
2223
)
23-
from ...long_running_tasks.models import TaskContext, TaskGet
24-
from ...long_running_tasks.task import RedisNamespace, RegisteredTaskName
24+
from ...long_running_tasks.models import (
25+
LRTNamespace,
26+
RegisteredTaskName,
27+
TaskContext,
28+
TaskGet,
29+
)
2530
from ..typing_extension import Handler
2631
from . import _routes
2732
from ._constants import (
@@ -63,7 +68,8 @@ async def start_long_running_task(
6368
task_id = None
6469
try:
6570
task_id = await lrt_api.start_task(
66-
long_running_manager.tasks_manager,
71+
long_running_manager.rpc_client,
72+
long_running_manager.lrt_namespace,
6773
registerd_task_name,
6874
fire_and_forget=fire_and_forget,
6975
task_context=task_context,
@@ -81,7 +87,7 @@ async def start_long_running_task(
8187
f"http://{ip_addr}:{port}{request_.app.router['get_task_result'].url_for(task_id=task_id)}" # NOSONAR
8288
)
8389
abort_url = TypeAdapter(AnyHttpUrl).validate_python(
84-
f"http://{ip_addr}:{port}{request_.app.router['cancel_and_delete_task'].url_for(task_id=task_id)}" # NOSONAR
90+
f"http://{ip_addr}:{port}{request_.app.router['remove_task'].url_for(task_id=task_id)}" # NOSONAR
8591
)
8692
task_get = TaskGet(
8793
task_id=task_id,
@@ -98,7 +104,11 @@ async def start_long_running_task(
98104
# remove the task, the client was disconnected
99105
if task_id:
100106
await lrt_api.remove_task(
101-
long_running_manager.tasks_manager, task_context, task_id
107+
long_running_manager.rpc_client,
108+
long_running_manager.lrt_namespace,
109+
task_context,
110+
task_id,
111+
wait_for_removal=True,
102112
)
103113
raise
104114

@@ -143,20 +153,23 @@ def setup(
143153
*,
144154
router_prefix: str,
145155
redis_settings: RedisSettings,
146-
redis_namespace: RedisNamespace,
147-
handler_check_decorator: Callable = _no_ops_decorator,
148-
task_request_context_decorator: Callable = _no_task_context_decorator,
156+
rabbit_settings: RabbitSettings,
157+
lrt_namespace: LRTNamespace,
149158
stale_task_check_interval: datetime.timedelta = DEFAULT_STALE_TASK_CHECK_INTERVAL,
150159
stale_task_detect_timeout: datetime.timedelta = DEFAULT_STALE_TASK_DETECT_TIMEOUT,
160+
handler_check_decorator: Callable = _no_ops_decorator,
161+
task_request_context_decorator: Callable = _no_task_context_decorator,
151162
) -> None:
152163
"""
153164
- `router_prefix` APIs are mounted on `/...`, this
154165
will change them to be mounted as `{router_prefix}/...`
155-
- `stale_task_check_interval_s` interval at which the
166+
- `redis_settings` settings for Redis connection
167+
- `rabbit_settings` settings for RabbitMQ connection
168+
- `lrt_namespace` namespace for the long-running tasks
169+
- `stale_task_check_interval` interval at which the
156170
TaskManager checks for tasks which are no longer being
157171
actively monitored by a client
158-
- `stale_task_detect_timeout_s` interval after which a
159-
task is considered stale
172+
- `stale_task_detect_timeout` interval after which atask is considered stale
160173
"""
161174

162175
async def on_cleanup_ctx(app: web.Application) -> AsyncGenerator[None, None]:
@@ -168,11 +181,11 @@ async def on_cleanup_ctx(app: web.Application) -> AsyncGenerator[None, None]:
168181
# add components to state
169182
app[APP_LONG_RUNNING_MANAGER_KEY] = long_running_manager = (
170183
AiohttpLongRunningManager(
171-
app=app,
172184
stale_task_check_interval=stale_task_check_interval,
173185
stale_task_detect_timeout=stale_task_detect_timeout,
174186
redis_settings=redis_settings,
175-
redis_namespace=redis_namespace,
187+
rabbit_settings=rabbit_settings,
188+
lrt_namespace=lrt_namespace,
176189
)
177190
)
178191

packages/service-library/src/servicelib/aiohttp/profiler_middleware.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
from aiohttp.web import HTTPInternalServerError, Request, StreamResponse, middleware
2-
from servicelib.mimetype_constants import (
3-
MIMETYPE_APPLICATION_JSON,
4-
MIMETYPE_APPLICATION_ND_JSON,
5-
)
62

3+
from ..mimetype_constants import MIMETYPE_APPLICATION_JSON, MIMETYPE_APPLICATION_ND_JSON
74
from ..utils_profiling_middleware import _is_profiling, _profiler, append_profile
85

96

packages/service-library/src/servicelib/fastapi/client_session.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22

33
import httpx
44
from fastapi import FastAPI
5-
from servicelib.fastapi.tracing import setup_httpx_client_tracing
65
from settings_library.tracing import TracingSettings
76

7+
from .tracing import setup_httpx_client_tracing
8+
89

910
def setup_client_session(
1011
app: FastAPI,

0 commit comments

Comments
 (0)