fractal-server 2.17.1a1__py3-none-any.whl → 2.18.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fractal_server/__init__.py +1 -1
- fractal_server/__main__.py +21 -19
- fractal_server/app/db/__init__.py +3 -3
- fractal_server/app/models/__init__.py +1 -0
- fractal_server/app/models/linkuserproject.py +43 -1
- fractal_server/app/models/security.py +28 -8
- fractal_server/app/models/v2/__init__.py +3 -1
- fractal_server/app/models/v2/accounting.py +9 -1
- fractal_server/app/models/v2/dataset.py +5 -1
- fractal_server/app/models/v2/history.py +15 -1
- fractal_server/app/models/v2/job.py +17 -2
- fractal_server/app/models/v2/profile.py +29 -0
- fractal_server/app/models/v2/project.py +4 -10
- fractal_server/app/models/v2/resource.py +17 -0
- fractal_server/app/models/v2/task_group.py +4 -3
- fractal_server/app/models/v2/workflow.py +2 -1
- fractal_server/app/routes/admin/v2/__init__.py +12 -13
- fractal_server/app/routes/admin/v2/accounting.py +3 -3
- fractal_server/app/routes/admin/v2/job.py +35 -24
- fractal_server/app/routes/admin/v2/profile.py +3 -2
- fractal_server/app/routes/admin/v2/resource.py +5 -5
- fractal_server/app/routes/admin/v2/sharing.py +103 -0
- fractal_server/app/routes/admin/v2/task.py +37 -26
- fractal_server/app/routes/admin/v2/task_group.py +94 -17
- fractal_server/app/routes/admin/v2/task_group_lifecycle.py +21 -22
- fractal_server/app/routes/api/__init__.py +1 -9
- fractal_server/app/routes/api/v2/__init__.py +49 -50
- fractal_server/app/routes/api/v2/_aux_functions.py +132 -124
- fractal_server/app/routes/api/v2/_aux_functions_history.py +51 -23
- fractal_server/app/routes/api/v2/_aux_functions_sharing.py +97 -0
- fractal_server/app/routes/api/v2/_aux_functions_task_lifecycle.py +6 -8
- fractal_server/app/routes/api/v2/_aux_functions_tasks.py +7 -9
- fractal_server/app/routes/api/v2/_aux_task_group_disambiguation.py +1 -2
- fractal_server/app/routes/api/v2/dataset.py +95 -102
- fractal_server/app/routes/api/v2/history.py +59 -33
- fractal_server/app/routes/api/v2/images.py +24 -9
- fractal_server/app/routes/api/v2/job.py +52 -33
- fractal_server/app/routes/api/v2/pre_submission_checks.py +16 -8
- fractal_server/app/routes/api/v2/project.py +65 -37
- fractal_server/app/routes/api/v2/sharing.py +311 -0
- fractal_server/app/routes/api/v2/status_legacy.py +31 -41
- fractal_server/app/routes/api/v2/submit.py +82 -78
- fractal_server/app/routes/api/v2/task.py +19 -20
- fractal_server/app/routes/api/v2/task_collection.py +41 -43
- fractal_server/app/routes/api/v2/task_collection_custom.py +19 -20
- fractal_server/app/routes/api/v2/task_collection_pixi.py +10 -11
- fractal_server/app/routes/api/v2/task_group.py +25 -24
- fractal_server/app/routes/api/v2/task_group_lifecycle.py +32 -32
- fractal_server/app/routes/api/v2/task_version_update.py +23 -19
- fractal_server/app/routes/api/v2/workflow.py +50 -55
- fractal_server/app/routes/api/v2/workflow_import.py +37 -37
- fractal_server/app/routes/api/v2/workflowtask.py +32 -26
- fractal_server/app/routes/auth/__init__.py +1 -3
- fractal_server/app/routes/auth/_aux_auth.py +101 -2
- fractal_server/app/routes/auth/current_user.py +2 -66
- fractal_server/app/routes/auth/group.py +8 -35
- fractal_server/app/routes/auth/login.py +1 -0
- fractal_server/app/routes/auth/oauth.py +4 -3
- fractal_server/app/routes/auth/register.py +4 -2
- fractal_server/app/routes/auth/router.py +2 -0
- fractal_server/app/routes/auth/users.py +19 -10
- fractal_server/app/routes/auth/viewer_paths.py +43 -0
- fractal_server/app/routes/aux/_job.py +1 -1
- fractal_server/app/routes/aux/_runner.py +2 -2
- fractal_server/app/routes/pagination.py +1 -1
- fractal_server/app/schemas/user.py +29 -12
- fractal_server/app/schemas/user_group.py +0 -15
- fractal_server/app/schemas/v2/__init__.py +55 -48
- fractal_server/app/schemas/v2/accounting.py +11 -0
- fractal_server/app/schemas/v2/dataset.py +57 -11
- fractal_server/app/schemas/v2/dumps.py +10 -9
- fractal_server/app/schemas/v2/job.py +11 -11
- fractal_server/app/schemas/v2/manifest.py +4 -3
- fractal_server/app/schemas/v2/profile.py +53 -2
- fractal_server/app/schemas/v2/project.py +3 -3
- fractal_server/app/schemas/v2/resource.py +121 -16
- fractal_server/app/schemas/v2/sharing.py +99 -0
- fractal_server/app/schemas/v2/status_legacy.py +3 -3
- fractal_server/app/schemas/v2/task.py +6 -7
- fractal_server/app/schemas/v2/task_collection.py +5 -5
- fractal_server/app/schemas/v2/task_group.py +16 -16
- fractal_server/app/schemas/v2/workflow.py +16 -16
- fractal_server/app/schemas/v2/workflowtask.py +16 -15
- fractal_server/app/security/__init__.py +5 -8
- fractal_server/app/security/signup_email.py +4 -5
- fractal_server/app/shutdown.py +6 -6
- fractal_server/config/__init__.py +0 -6
- fractal_server/config/_data.py +0 -68
- fractal_server/config/_database.py +19 -20
- fractal_server/config/_email.py +30 -38
- fractal_server/config/_main.py +38 -52
- fractal_server/config/_oauth.py +17 -21
- fractal_server/data_migrations/2_18_0.py +30 -0
- fractal_server/exceptions.py +4 -0
- fractal_server/images/models.py +4 -5
- fractal_server/images/status_tools.py +4 -2
- fractal_server/logger.py +1 -1
- fractal_server/main.py +75 -13
- fractal_server/migrations/versions/034a469ec2eb_task_groups.py +4 -8
- fractal_server/migrations/versions/091b01f51f88_add_usergroup_and_linkusergroup_table.py +1 -1
- fractal_server/migrations/versions/0f5f85bb2ae7_add_pre_pinned_packages.py +1 -0
- fractal_server/migrations/versions/19eca0dd47a9_user_settings_project_dir.py +1 -1
- fractal_server/migrations/versions/1a83a5260664_rename.py +1 -1
- fractal_server/migrations/versions/1eac13a26c83_drop_v1_tables.py +1 -0
- fractal_server/migrations/versions/316140ff7ee1_remove_usersettings_cache_dir.py +1 -1
- fractal_server/migrations/versions/40d6d6511b20_add_index_to_history_models.py +47 -0
- fractal_server/migrations/versions/45fbb391d7af_make_resource_id_fk_non_nullable.py +1 -1
- fractal_server/migrations/versions/47351f8c7ebc_drop_dataset_filters.py +1 -0
- fractal_server/migrations/versions/49d0856e9569_drop_table.py +2 -3
- fractal_server/migrations/versions/4c308bcaea2b_add_task_args_schema_and_task_args_.py +1 -1
- fractal_server/migrations/versions/4cedeb448a53_workflowtask_foreign_keys_not_nullables.py +1 -1
- fractal_server/migrations/versions/501961cfcd85_remove_link_between_v1_and_v2_tasks_.py +2 -1
- fractal_server/migrations/versions/50a13d6138fd_initial_schema.py +7 -19
- fractal_server/migrations/versions/5bf02391cfef_v2.py +4 -10
- fractal_server/migrations/versions/70e77f1c38b0_add_applyworkflow_first_task_index_and_.py +1 -0
- fractal_server/migrations/versions/71eefd1dd202_add_slurm_accounts.py +1 -1
- fractal_server/migrations/versions/7673fe18c05d_remove_project_dir_server_default.py +1 -1
- fractal_server/migrations/versions/7910eed4cf97_user_project_dirs_and_usergroup_viewer_.py +60 -0
- fractal_server/migrations/versions/791ce783d3d8_add_indices.py +1 -1
- fractal_server/migrations/versions/83bc2ad3ffcc_2_17_0.py +1 -0
- fractal_server/migrations/versions/84bf0fffde30_add_dumps_to_applyworkflow.py +1 -0
- fractal_server/migrations/versions/88270f589c9b_add_prevent_new_submissions.py +39 -0
- fractal_server/migrations/versions/8e8f227a3e36_update_taskv2_post_2_7_0.py +2 -4
- fractal_server/migrations/versions/8f79bd162e35_add_docs_info_and_docs_link_to_task_.py +1 -1
- fractal_server/migrations/versions/94a47ea2d3ff_remove_cache_dir_slurm_user_and_slurm_.py +1 -0
- fractal_server/migrations/versions/969d84257cac_add_historyrun_task_id.py +1 -1
- fractal_server/migrations/versions/97f444d47249_add_applyworkflow_project_dump.py +1 -1
- fractal_server/migrations/versions/981d588fe248_add_executor_error_log.py +1 -1
- fractal_server/migrations/versions/99ea79d9e5d2_add_dataset_history.py +2 -4
- fractal_server/migrations/versions/9c5ae74c9b98_add_user_settings_table.py +1 -1
- fractal_server/migrations/versions/9db60297b8b2_set_ondelete.py +1 -1
- fractal_server/migrations/versions/9fd26a2b0de4_add_workflow_timestamp_created.py +1 -1
- fractal_server/migrations/versions/a7f4d6137b53_add_workflow_dump_to_applyworkflow.py +1 -1
- fractal_server/migrations/versions/af1ef1c83c9b_add_accounting_tables.py +1 -0
- fractal_server/migrations/versions/af8673379a5c_drop_old_filter_columns.py +1 -0
- fractal_server/migrations/versions/b1e7f7a1ff71_task_group_for_pixi.py +1 -1
- fractal_server/migrations/versions/b3ffb095f973_json_to_jsonb.py +1 -0
- fractal_server/migrations/versions/bc0e8b3327a7_project_sharing.py +72 -0
- fractal_server/migrations/versions/c90a7c76e996_job_id_in_history_run.py +1 -1
- fractal_server/migrations/versions/caba9fb1ea5e_drop_useroauth_user_settings_id.py +1 -1
- fractal_server/migrations/versions/d256a7379ab8_taskgroup_activity_and_venv_info_to_.py +4 -9
- fractal_server/migrations/versions/d4fe3708d309_make_applyworkflow_workflow_dump_non_.py +1 -0
- fractal_server/migrations/versions/da2cb2ac4255_user_group_viewer_paths.py +1 -1
- fractal_server/migrations/versions/db09233ad13a_split_filters_and_keep_old_columns.py +1 -0
- fractal_server/migrations/versions/e0e717ae2f26_delete_linkuserproject_ondelete_project.py +50 -0
- fractal_server/migrations/versions/e75cac726012_make_applyworkflow_start_timestamp_not_.py +1 -0
- fractal_server/migrations/versions/e81103413827_add_job_type_filters.py +1 -1
- fractal_server/migrations/versions/efa89c30e0a4_add_project_timestamp_created.py +1 -0
- fractal_server/migrations/versions/f0702066b007_one_submitted_job_per_dataset.py +40 -0
- fractal_server/migrations/versions/f37aceb45062_make_historyunit_logfile_required.py +1 -1
- fractal_server/migrations/versions/f384e1c0cf5d_drop_task_default_args_columns.py +1 -0
- fractal_server/migrations/versions/fbce16ff4e47_new_history_items.py +4 -9
- fractal_server/runner/config/_local.py +8 -5
- fractal_server/runner/config/_slurm.py +39 -33
- fractal_server/runner/config/slurm_mem_to_MB.py +0 -1
- fractal_server/runner/executors/base_runner.py +29 -4
- fractal_server/runner/executors/local/get_local_config.py +1 -0
- fractal_server/runner/executors/local/runner.py +14 -13
- fractal_server/runner/executors/slurm_common/_batching.py +9 -20
- fractal_server/runner/executors/slurm_common/base_slurm_runner.py +53 -27
- fractal_server/runner/executors/slurm_common/get_slurm_config.py +14 -7
- fractal_server/runner/executors/slurm_common/remote.py +3 -1
- fractal_server/runner/executors/slurm_common/slurm_config.py +2 -0
- fractal_server/runner/executors/slurm_common/slurm_job_task_models.py +1 -3
- fractal_server/runner/executors/slurm_ssh/runner.py +16 -11
- fractal_server/runner/executors/slurm_ssh/tar_commands.py +1 -0
- fractal_server/runner/executors/slurm_sudo/_subprocess_run_as_user.py +1 -0
- fractal_server/runner/executors/slurm_sudo/runner.py +16 -11
- fractal_server/runner/task_files.py +9 -3
- fractal_server/runner/v2/_local.py +12 -6
- fractal_server/runner/v2/_slurm_ssh.py +14 -7
- fractal_server/runner/v2/_slurm_sudo.py +14 -7
- fractal_server/runner/v2/db_tools.py +0 -1
- fractal_server/runner/v2/deduplicate_list.py +2 -1
- fractal_server/runner/v2/runner.py +44 -28
- fractal_server/runner/v2/runner_functions.py +22 -28
- fractal_server/runner/v2/submit_workflow.py +29 -15
- fractal_server/ssh/_fabric.py +6 -13
- fractal_server/string_tools.py +0 -1
- fractal_server/syringe.py +1 -1
- fractal_server/tasks/config/_pixi.py +1 -1
- fractal_server/tasks/config/_python.py +16 -9
- fractal_server/tasks/utils.py +0 -1
- fractal_server/tasks/v2/local/_utils.py +3 -3
- fractal_server/tasks/v2/local/collect.py +15 -18
- fractal_server/tasks/v2/local/collect_pixi.py +14 -16
- fractal_server/tasks/v2/local/deactivate.py +14 -15
- fractal_server/tasks/v2/local/deactivate_pixi.py +7 -7
- fractal_server/tasks/v2/local/delete.py +6 -8
- fractal_server/tasks/v2/local/reactivate.py +12 -12
- fractal_server/tasks/v2/local/reactivate_pixi.py +12 -12
- fractal_server/tasks/v2/ssh/_utils.py +3 -3
- fractal_server/tasks/v2/ssh/collect.py +19 -24
- fractal_server/tasks/v2/ssh/collect_pixi.py +22 -24
- fractal_server/tasks/v2/ssh/deactivate.py +17 -15
- fractal_server/tasks/v2/ssh/deactivate_pixi.py +8 -7
- fractal_server/tasks/v2/ssh/delete.py +12 -10
- fractal_server/tasks/v2/ssh/reactivate.py +16 -16
- fractal_server/tasks/v2/ssh/reactivate_pixi.py +13 -14
- fractal_server/tasks/v2/templates/1_create_venv.sh +2 -0
- fractal_server/tasks/v2/templates/2_pip_install.sh +2 -0
- fractal_server/tasks/v2/templates/3_pip_freeze.sh +2 -0
- fractal_server/tasks/v2/templates/4_pip_show.sh +2 -0
- fractal_server/tasks/v2/templates/5_get_venv_size_and_file_number.sh +3 -1
- fractal_server/tasks/v2/templates/6_pip_install_from_freeze.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_1_extract.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_2_install.sh +2 -0
- fractal_server/tasks/v2/templates/pixi_3_post_install.sh +2 -0
- fractal_server/tasks/v2/utils_background.py +10 -10
- fractal_server/tasks/v2/utils_database.py +5 -5
- fractal_server/tasks/v2/utils_package_names.py +1 -2
- fractal_server/tasks/v2/utils_pixi.py +1 -3
- fractal_server/types/__init__.py +98 -1
- fractal_server/types/validators/__init__.py +3 -0
- fractal_server/types/validators/_common_validators.py +33 -3
- fractal_server/types/validators/_workflow_task_arguments_validators.py +1 -2
- fractal_server/utils.py +1 -0
- fractal_server/zip_tools.py +34 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/METADATA +3 -2
- fractal_server-2.18.0.dist-info/RECORD +275 -0
- fractal_server/app/routes/admin/v2/project.py +0 -41
- fractal_server-2.17.1a1.dist-info/RECORD +0 -264
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/WHEEL +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/entry_points.txt +0 -0
- {fractal_server-2.17.1a1.dist-info → fractal_server-2.18.0.dist-info}/licenses/LICENSE +0 -0
fractal_server/images/models.py
CHANGED
|
@@ -5,11 +5,10 @@ from fractal_server.types import DictStrAny
|
|
|
5
5
|
from fractal_server.types import ImageAttributes
|
|
6
6
|
from fractal_server.types import ImageAttributesWithNone
|
|
7
7
|
from fractal_server.types import ImageTypes
|
|
8
|
-
from fractal_server.types import ZarrDirStr
|
|
9
8
|
from fractal_server.types import ZarrUrlStr
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
class
|
|
11
|
+
class SingleImageBase(BaseModel):
|
|
13
12
|
"""
|
|
14
13
|
Base for SingleImage and SingleImageTaskOutput.
|
|
15
14
|
|
|
@@ -21,13 +20,13 @@ class _SingleImageBase(BaseModel):
|
|
|
21
20
|
"""
|
|
22
21
|
|
|
23
22
|
zarr_url: ZarrUrlStr
|
|
24
|
-
origin:
|
|
23
|
+
origin: ZarrUrlStr | None = None
|
|
25
24
|
|
|
26
25
|
attributes: DictStrAny = Field(default_factory=dict)
|
|
27
26
|
types: ImageTypes = Field(default_factory=dict)
|
|
28
27
|
|
|
29
28
|
|
|
30
|
-
class SingleImageTaskOutput(
|
|
29
|
+
class SingleImageTaskOutput(SingleImageBase):
|
|
31
30
|
"""
|
|
32
31
|
`SingleImageBase`, with scalar `attributes` values (`None` included).
|
|
33
32
|
"""
|
|
@@ -35,7 +34,7 @@ class SingleImageTaskOutput(_SingleImageBase):
|
|
|
35
34
|
attributes: ImageAttributesWithNone = Field(default_factory=dict)
|
|
36
35
|
|
|
37
36
|
|
|
38
|
-
class SingleImage(
|
|
37
|
+
class SingleImage(SingleImageBase):
|
|
39
38
|
"""
|
|
40
39
|
`SingleImageBase`, with scalar `attributes` values (`None` excluded).
|
|
41
40
|
"""
|
|
@@ -37,10 +37,12 @@ def _prepare_query(
|
|
|
37
37
|
"""
|
|
38
38
|
stm = (
|
|
39
39
|
select(HistoryImageCache.zarr_url, HistoryUnit.status)
|
|
40
|
-
.join(
|
|
40
|
+
.join(
|
|
41
|
+
HistoryUnit,
|
|
42
|
+
HistoryImageCache.latest_history_unit_id == HistoryUnit.id,
|
|
43
|
+
)
|
|
41
44
|
.where(HistoryImageCache.dataset_id == dataset_id)
|
|
42
45
|
.where(HistoryImageCache.workflowtask_id == workflowtask_id)
|
|
43
|
-
.where(HistoryImageCache.latest_history_unit_id == HistoryUnit.id)
|
|
44
46
|
)
|
|
45
47
|
return stm
|
|
46
48
|
|
fractal_server/logger.py
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
"""
|
|
13
13
|
This module provides logging utilities
|
|
14
14
|
"""
|
|
15
|
+
|
|
15
16
|
import logging
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
|
|
18
19
|
from .config import get_settings
|
|
19
20
|
from .syringe import Inject
|
|
20
21
|
|
|
21
|
-
|
|
22
22
|
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
|
23
23
|
LOG_FORMATTER = logging.Formatter(LOG_FORMAT)
|
|
24
24
|
|
fractal_server/main.py
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import time
|
|
2
3
|
from contextlib import asynccontextmanager
|
|
4
|
+
from datetime import datetime
|
|
3
5
|
from itertools import chain
|
|
4
6
|
|
|
5
7
|
from fastapi import FastAPI
|
|
8
|
+
from starlette.types import Message
|
|
9
|
+
from starlette.types import Receive
|
|
10
|
+
from starlette.types import Scope
|
|
11
|
+
from starlette.types import Send
|
|
12
|
+
|
|
13
|
+
from fractal_server import __VERSION__
|
|
14
|
+
from fractal_server.app.schemas.v2 import ResourceType
|
|
6
15
|
|
|
7
16
|
from .app.routes.aux._runner import _backend_supports_shutdown
|
|
8
17
|
from .app.shutdown import cleanup_after_shutdown
|
|
9
|
-
from .config import get_data_settings
|
|
10
18
|
from .config import get_db_settings
|
|
11
19
|
from .config import get_email_settings
|
|
12
20
|
from .config import get_settings
|
|
@@ -15,8 +23,6 @@ from .logger import get_logger
|
|
|
15
23
|
from .logger import reset_logger_handlers
|
|
16
24
|
from .logger import set_logger
|
|
17
25
|
from .syringe import Inject
|
|
18
|
-
from fractal_server import __VERSION__
|
|
19
|
-
from fractal_server.app.schemas.v2 import ResourceType
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
def collect_routers(app: FastAPI) -> None:
|
|
@@ -27,16 +33,14 @@ def collect_routers(app: FastAPI) -> None:
|
|
|
27
33
|
app:
|
|
28
34
|
The application to register the routers to.
|
|
29
35
|
"""
|
|
36
|
+
from .app.routes.admin.v2 import router_admin
|
|
30
37
|
from .app.routes.api import router_api
|
|
31
|
-
from .app.routes.api.v2 import router_api_v2
|
|
32
|
-
from .app.routes.admin.v2 import router_admin_v2
|
|
38
|
+
from .app.routes.api.v2 import router_api as router_api_v2
|
|
33
39
|
from .app.routes.auth.router import router_auth
|
|
34
40
|
|
|
35
41
|
app.include_router(router_api, prefix="/api")
|
|
36
42
|
app.include_router(router_api_v2, prefix="/api/v2")
|
|
37
|
-
app.include_router(
|
|
38
|
-
router_admin_v2, prefix="/admin/v2", tags=["V2 Admin area"]
|
|
39
|
-
)
|
|
43
|
+
app.include_router(router_admin, prefix="/admin/v2", tags=["Admin area"])
|
|
40
44
|
app.include_router(router_auth, prefix="/auth", tags=["Authentication"])
|
|
41
45
|
|
|
42
46
|
|
|
@@ -53,14 +57,12 @@ def check_settings() -> None:
|
|
|
53
57
|
settings = Inject(get_settings)
|
|
54
58
|
db_settings = Inject(get_db_settings)
|
|
55
59
|
email_settings = Inject(get_email_settings)
|
|
56
|
-
data_settings = Inject(get_data_settings)
|
|
57
60
|
logger = set_logger("fractal_server_settings")
|
|
58
61
|
logger.debug("Fractal Settings:")
|
|
59
62
|
for key, value in chain(
|
|
60
63
|
db_settings.model_dump().items(),
|
|
61
64
|
settings.model_dump().items(),
|
|
62
65
|
email_settings.model_dump().items(),
|
|
63
|
-
data_settings.model_dump().items(),
|
|
64
66
|
):
|
|
65
67
|
if any(s in key.upper() for s in ["PASSWORD", "SECRET", "KEY"]):
|
|
66
68
|
value = "*****"
|
|
@@ -70,7 +72,7 @@ def check_settings() -> None:
|
|
|
70
72
|
|
|
71
73
|
@asynccontextmanager
|
|
72
74
|
async def lifespan(app: FastAPI):
|
|
73
|
-
app.state.
|
|
75
|
+
app.state.jobs = []
|
|
74
76
|
logger = set_logger("fractal_server.lifespan")
|
|
75
77
|
logger.info(f"[startup] START (fractal-server {__VERSION__})")
|
|
76
78
|
check_settings()
|
|
@@ -107,12 +109,12 @@ async def lifespan(app: FastAPI):
|
|
|
107
109
|
|
|
108
110
|
logger.info(
|
|
109
111
|
f"[teardown] Current worker with pid {os.getpid()} is shutting down. "
|
|
110
|
-
f"Current jobs: {app.state.
|
|
112
|
+
f"Current jobs: {app.state.jobs=}"
|
|
111
113
|
)
|
|
112
114
|
if _backend_supports_shutdown(settings.FRACTAL_RUNNER_BACKEND):
|
|
113
115
|
try:
|
|
114
116
|
await cleanup_after_shutdown(
|
|
115
|
-
|
|
117
|
+
jobs=app.state.jobs,
|
|
116
118
|
logger_name="fractal_server.lifespan",
|
|
117
119
|
)
|
|
118
120
|
except Exception as e:
|
|
@@ -130,6 +132,59 @@ async def lifespan(app: FastAPI):
|
|
|
130
132
|
reset_logger_handlers(logger)
|
|
131
133
|
|
|
132
134
|
|
|
135
|
+
slow_response_logger = set_logger("slow-response")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _endpoint_has_background_task(method: str, path: str) -> bool:
|
|
139
|
+
has_background_task = (method == "POST") and (
|
|
140
|
+
"/job/submit/" in path
|
|
141
|
+
or "/task/collect/pi" in path # "/pip" and "/pixi"
|
|
142
|
+
or "/task-group/" in path
|
|
143
|
+
)
|
|
144
|
+
return has_background_task
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class SlowResponseMiddleware:
|
|
148
|
+
def __init__(self, app: FastAPI, time_threshold: float):
|
|
149
|
+
self.app = app
|
|
150
|
+
self.time_threshold = time_threshold
|
|
151
|
+
|
|
152
|
+
async def __call__(self, scope: Scope, receive: Receive, send: Send):
|
|
153
|
+
if (
|
|
154
|
+
scope["type"] != "http" # e.g. `scope["type"] == "lifespan"`
|
|
155
|
+
or _endpoint_has_background_task(scope["method"], scope["path"])
|
|
156
|
+
):
|
|
157
|
+
await self.app(scope, receive, send)
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
# Mutable variable which can be updated from within `send_wrapper`
|
|
161
|
+
context = {"status_code": None}
|
|
162
|
+
|
|
163
|
+
async def send_wrapper(message: Message):
|
|
164
|
+
if message["type"] == "http.response.start":
|
|
165
|
+
context["status_code"] = message["status"]
|
|
166
|
+
await send(message)
|
|
167
|
+
|
|
168
|
+
# Measure request time
|
|
169
|
+
start_timestamp = datetime.now()
|
|
170
|
+
start_time = time.perf_counter()
|
|
171
|
+
await self.app(scope, receive, send_wrapper)
|
|
172
|
+
stop_time = time.perf_counter()
|
|
173
|
+
request_time = stop_time - start_time
|
|
174
|
+
|
|
175
|
+
# Log if process time is too high
|
|
176
|
+
if request_time > self.time_threshold:
|
|
177
|
+
end_timestamp = datetime.now()
|
|
178
|
+
slow_response_logger.warning(
|
|
179
|
+
f"{scope['method']} {scope['route'].path}"
|
|
180
|
+
f"?{scope['query_string'].decode('utf-8')}, "
|
|
181
|
+
f"{context['status_code']}, "
|
|
182
|
+
f"{request_time:.2f}, "
|
|
183
|
+
f"{start_timestamp.isoformat(timespec='milliseconds')}, "
|
|
184
|
+
f"{end_timestamp.isoformat(timespec='milliseconds')}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
133
188
|
def start_application() -> FastAPI:
|
|
134
189
|
"""
|
|
135
190
|
Create the application, initialise it and collect all available routers.
|
|
@@ -139,6 +194,13 @@ def start_application() -> FastAPI:
|
|
|
139
194
|
The fully initialised application.
|
|
140
195
|
"""
|
|
141
196
|
app = FastAPI(lifespan=lifespan)
|
|
197
|
+
|
|
198
|
+
settings = Inject(get_settings)
|
|
199
|
+
app.add_middleware(
|
|
200
|
+
SlowResponseMiddleware,
|
|
201
|
+
time_threshold=settings.FRACTAL_LONG_REQUEST_TIME,
|
|
202
|
+
)
|
|
203
|
+
|
|
142
204
|
collect_routers(app)
|
|
143
205
|
return app
|
|
144
206
|
|
|
@@ -5,6 +5,7 @@ Revises: da2cb2ac4255
|
|
|
5
5
|
Create Date: 2024-10-10 16:14:13.976231
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
from datetime import datetime
|
|
9
10
|
from datetime import timezone
|
|
10
11
|
|
|
@@ -12,7 +13,6 @@ import sqlalchemy as sa
|
|
|
12
13
|
import sqlmodel
|
|
13
14
|
from alembic import op
|
|
14
15
|
|
|
15
|
-
|
|
16
16
|
# revision identifiers, used by Alembic.
|
|
17
17
|
revision = "034a469ec2eb"
|
|
18
18
|
down_revision = "da2cb2ac4255"
|
|
@@ -26,15 +26,11 @@ def upgrade() -> None:
|
|
|
26
26
|
sa.Column("id", sa.Integer(), nullable=False),
|
|
27
27
|
sa.Column("user_id", sa.Integer(), nullable=False),
|
|
28
28
|
sa.Column("user_group_id", sa.Integer(), nullable=True),
|
|
29
|
-
sa.Column(
|
|
30
|
-
"origin", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
31
|
-
),
|
|
29
|
+
sa.Column("origin", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
32
30
|
sa.Column(
|
|
33
31
|
"pkg_name", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
34
32
|
),
|
|
35
|
-
sa.Column(
|
|
36
|
-
"version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
37
|
-
),
|
|
33
|
+
sa.Column("version", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
38
34
|
sa.Column(
|
|
39
35
|
"python_version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
40
36
|
),
|
|
@@ -123,8 +119,8 @@ def upgrade() -> None:
|
|
|
123
119
|
except BaseException as e:
|
|
124
120
|
if op.get_bind().dialect.name != "sqlite":
|
|
125
121
|
raise e
|
|
126
|
-
import sqlite3
|
|
127
122
|
import logging
|
|
123
|
+
import sqlite3
|
|
128
124
|
|
|
129
125
|
logger = logging.getLogger("alembic.runtime.migration")
|
|
130
126
|
logger.warning(
|
|
@@ -5,11 +5,11 @@ Revises: 5bf02391cfef
|
|
|
5
5
|
Create Date: 2024-09-09 13:17:51.008231
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "091b01f51f88"
|
|
15
15
|
down_revision = "5bf02391cfef"
|
|
@@ -5,11 +5,11 @@ Revises: 8e8f227a3e36
|
|
|
5
5
|
Create Date: 2024-10-30 14:34:28.219355
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "19eca0dd47a9"
|
|
15
15
|
down_revision = "8e8f227a3e36"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""add index to history models
|
|
2
|
+
|
|
3
|
+
Revision ID: 40d6d6511b20
|
|
4
|
+
Revises: 7673fe18c05d
|
|
5
|
+
Create Date: 2025-11-14 10:34:12.300920
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from alembic import op
|
|
10
|
+
|
|
11
|
+
# revision identifiers, used by Alembic.
|
|
12
|
+
revision = "40d6d6511b20"
|
|
13
|
+
down_revision = "7673fe18c05d"
|
|
14
|
+
branch_labels = None
|
|
15
|
+
depends_on = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def upgrade() -> None:
|
|
19
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
20
|
+
with op.batch_alter_table("historyimagecache", schema=None) as batch_op:
|
|
21
|
+
batch_op.create_index(
|
|
22
|
+
batch_op.f("ix_historyimagecache_latest_history_unit_id"),
|
|
23
|
+
["latest_history_unit_id"],
|
|
24
|
+
unique=False,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
with op.batch_alter_table("historyunit", schema=None) as batch_op:
|
|
28
|
+
batch_op.create_index(
|
|
29
|
+
batch_op.f("ix_historyunit_history_run_id"),
|
|
30
|
+
["history_run_id"],
|
|
31
|
+
unique=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# ### end Alembic commands ###
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def downgrade() -> None:
|
|
38
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
|
39
|
+
with op.batch_alter_table("historyunit", schema=None) as batch_op:
|
|
40
|
+
batch_op.drop_index(batch_op.f("ix_historyunit_history_run_id"))
|
|
41
|
+
|
|
42
|
+
with op.batch_alter_table("historyimagecache", schema=None) as batch_op:
|
|
43
|
+
batch_op.drop_index(
|
|
44
|
+
batch_op.f("ix_historyimagecache_latest_history_unit_id")
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# ### end Alembic commands ###
|
|
@@ -5,6 +5,7 @@ Revises: 45fbb391d7af
|
|
|
5
5
|
Create Date: 2025-11-11 16:39:41.497832
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
from alembic import op
|
|
10
11
|
from sqlalchemy.dialects import postgresql
|
|
@@ -34,9 +35,7 @@ def downgrade() -> None:
|
|
|
34
35
|
autoincrement=False,
|
|
35
36
|
nullable=False,
|
|
36
37
|
),
|
|
37
|
-
sa.Column(
|
|
38
|
-
"ssh_host", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
39
|
-
),
|
|
38
|
+
sa.Column("ssh_host", sa.VARCHAR(), autoincrement=False, nullable=True),
|
|
40
39
|
sa.Column(
|
|
41
40
|
"ssh_username", sa.VARCHAR(), autoincrement=False, nullable=True
|
|
42
41
|
),
|
|
@@ -5,11 +5,11 @@ Revises: 50a13d6138fd
|
|
|
5
5
|
Create Date: 2023-05-29 17:09:02.492639
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "4c308bcaea2b"
|
|
15
15
|
down_revision = "50a13d6138fd"
|
|
@@ -5,6 +5,7 @@ Revises: 5bf02391cfef
|
|
|
5
5
|
Create Date: 2024-09-09 14:15:34.415926
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
from alembic import op
|
|
10
11
|
|
|
@@ -36,8 +37,8 @@ def upgrade() -> None:
|
|
|
36
37
|
# NOTE: in sqlite, the `drop_index` command fails if the existing table
|
|
37
38
|
# has zero rows, while it succeeds if there are already some rows
|
|
38
39
|
if op.get_bind().dialect.name == "sqlite":
|
|
39
|
-
import sqlite3
|
|
40
40
|
import logging
|
|
41
|
+
import sqlite3
|
|
41
42
|
|
|
42
43
|
logger = logging.getLogger("alembic.runtime.migration")
|
|
43
44
|
logger.warning(
|
|
@@ -5,11 +5,11 @@ Revises:
|
|
|
5
5
|
Create Date: 2023-05-29 12:14:56.670243
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "50a13d6138fd"
|
|
15
15
|
down_revision = None
|
|
@@ -37,9 +37,7 @@ def upgrade() -> None:
|
|
|
37
37
|
"task",
|
|
38
38
|
sa.Column("default_args", sa.JSON(), nullable=True),
|
|
39
39
|
sa.Column("meta", sa.JSON(), nullable=True),
|
|
40
|
-
sa.Column(
|
|
41
|
-
"source", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
42
|
-
),
|
|
40
|
+
sa.Column("source", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
43
41
|
sa.Column("id", sa.Integer(), nullable=False),
|
|
44
42
|
sa.Column("name", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
45
43
|
sa.Column(
|
|
@@ -52,9 +50,7 @@ def upgrade() -> None:
|
|
|
52
50
|
"output_type", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
53
51
|
),
|
|
54
52
|
sa.Column("owner", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
55
|
-
sa.Column(
|
|
56
|
-
"version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
57
|
-
),
|
|
53
|
+
sa.Column("version", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
58
54
|
sa.PrimaryKeyConstraint("id"),
|
|
59
55
|
sa.UniqueConstraint("source"),
|
|
60
56
|
)
|
|
@@ -163,9 +159,7 @@ def upgrade() -> None:
|
|
|
163
159
|
)
|
|
164
160
|
op.create_table(
|
|
165
161
|
"applyworkflow",
|
|
166
|
-
sa.Column(
|
|
167
|
-
"start_timestamp", sa.DateTime(timezone=True), nullable=True
|
|
168
|
-
),
|
|
162
|
+
sa.Column("start_timestamp", sa.DateTime(timezone=True), nullable=True),
|
|
169
163
|
sa.Column("end_timestamp", sa.DateTime(timezone=True), nullable=True),
|
|
170
164
|
sa.Column(
|
|
171
165
|
"worker_init", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
@@ -183,9 +177,7 @@ def upgrade() -> None:
|
|
|
183
177
|
sqlmodel.sql.sqltypes.AutoString(),
|
|
184
178
|
nullable=True,
|
|
185
179
|
),
|
|
186
|
-
sa.Column(
|
|
187
|
-
"status", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
188
|
-
),
|
|
180
|
+
sa.Column("status", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
189
181
|
sa.Column("log", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
190
182
|
sa.ForeignKeyConstraint(
|
|
191
183
|
["input_dataset_id"],
|
|
@@ -243,12 +235,8 @@ def downgrade() -> None:
|
|
|
243
235
|
op.drop_table("resource")
|
|
244
236
|
op.drop_table("applyworkflow")
|
|
245
237
|
op.drop_table("workflow")
|
|
246
|
-
op.drop_index(
|
|
247
|
-
|
|
248
|
-
)
|
|
249
|
-
op.drop_index(
|
|
250
|
-
op.f("ix_oauthaccount_account_id"), table_name="oauthaccount"
|
|
251
|
-
)
|
|
238
|
+
op.drop_index(op.f("ix_oauthaccount_oauth_name"), table_name="oauthaccount")
|
|
239
|
+
op.drop_index(op.f("ix_oauthaccount_account_id"), table_name="oauthaccount")
|
|
252
240
|
op.drop_table("oauthaccount")
|
|
253
241
|
op.drop_table("linkuserproject")
|
|
254
242
|
op.drop_table("dataset")
|
|
@@ -5,11 +5,11 @@ Revises: 9fd26a2b0de4
|
|
|
5
5
|
Create Date: 2024-04-18 10:35:19.067833
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "5bf02391cfef"
|
|
15
15
|
down_revision = "9fd26a2b0de4"
|
|
@@ -50,9 +50,7 @@ def upgrade() -> None:
|
|
|
50
50
|
sqlmodel.sql.sqltypes.AutoString(),
|
|
51
51
|
nullable=True,
|
|
52
52
|
),
|
|
53
|
-
sa.Column(
|
|
54
|
-
"source", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
55
|
-
),
|
|
53
|
+
sa.Column("source", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
56
54
|
sa.Column(
|
|
57
55
|
"meta_non_parallel", sa.JSON(), server_default="{}", nullable=False
|
|
58
56
|
),
|
|
@@ -60,9 +58,7 @@ def upgrade() -> None:
|
|
|
60
58
|
"meta_parallel", sa.JSON(), server_default="{}", nullable=False
|
|
61
59
|
),
|
|
62
60
|
sa.Column("owner", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
63
|
-
sa.Column(
|
|
64
|
-
"version", sqlmodel.sql.sqltypes.AutoString(), nullable=True
|
|
65
|
-
),
|
|
61
|
+
sa.Column("version", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
66
62
|
sa.Column("args_schema_non_parallel", sa.JSON(), nullable=True),
|
|
67
63
|
sa.Column("args_schema_parallel", sa.JSON(), nullable=True),
|
|
68
64
|
sa.Column(
|
|
@@ -166,9 +162,7 @@ def upgrade() -> None:
|
|
|
166
162
|
"start_timestamp", sa.DateTime(timezone=True), nullable=False
|
|
167
163
|
),
|
|
168
164
|
sa.Column("end_timestamp", sa.DateTime(timezone=True), nullable=True),
|
|
169
|
-
sa.Column(
|
|
170
|
-
"status", sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
|
171
|
-
),
|
|
165
|
+
sa.Column("status", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
|
172
166
|
sa.Column("log", sqlmodel.sql.sqltypes.AutoString(), nullable=True),
|
|
173
167
|
sa.ForeignKeyConstraint(
|
|
174
168
|
["dataset_id"],
|
|
@@ -5,11 +5,11 @@ Revises: d4fe3708d309
|
|
|
5
5
|
Create Date: 2023-12-05 12:36:44.100065
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
|
+
|
|
8
9
|
import sqlalchemy as sa
|
|
9
10
|
import sqlmodel
|
|
10
11
|
from alembic import op
|
|
11
12
|
|
|
12
|
-
|
|
13
13
|
# revision identifiers, used by Alembic.
|
|
14
14
|
revision = "71eefd1dd202"
|
|
15
15
|
down_revision = "d4fe3708d309"
|