Skip to content

Commit 63d3a01

Browse files
chrisburrchaen
andauthored
feat: add diracx-tasks (#842)
* feat: add diracx-tasks --------- Co-authored-by: Christophe Haen <christophe.haen@cern.ch>
1 parent 2b65dae commit 63d3a01

File tree

124 files changed

+14027
-1034
lines changed

Some content is hidden

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

124 files changed

+14027
-1034
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ docs/source/_build
9797

9898
# pixi environments
9999
.pixi
100+
101+
# run_local.sh state
102+
.run-local-env
100103
*.egg-info
101104
docs/templates/_builtin_markdown.jinja
102105

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ repos:
4848
- types-cachetools
4949
- types-requests
5050
- types-python-dateutil
51+
- types-croniter
5152
- types-aiobotocore[essential]
5253
- boto3-stubs[essential]
5354
exclude: ^(diracx-client/src/diracx/client/_generated|diracx-[a-z]+/tests/|diracx-testing/|build|extensions/gubbins/gubbins-client/src/gubbins/client/_generated)

containers/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ COPY diracx-client/ diracx-client/
1313
COPY diracx-core/ diracx-core/
1414
COPY diracx-db/ diracx-db/
1515
COPY diracx-logic/ diracx-logic/
16+
COPY diracx-tasks/ diracx-tasks/
1617
COPY diracx-routers/ diracx-routers/
1718
COPY diracx-testing/ diracx-testing/
1819
COPY pyproject.toml ./
@@ -23,6 +24,7 @@ COPY extensions/gubbins/gubbins-client/ extensions/gubbins/gubbins-client/
2324
COPY extensions/gubbins/gubbins-core/ extensions/gubbins/gubbins-core/
2425
COPY extensions/gubbins/gubbins-db/ extensions/gubbins/gubbins-db/
2526
COPY extensions/gubbins/gubbins-logic/ extensions/gubbins/gubbins-logic/
27+
COPY extensions/gubbins/gubbins-tasks/ extensions/gubbins/gubbins-tasks/
2628
COPY extensions/gubbins/gubbins-routers/ extensions/gubbins/gubbins-routers/
2729
COPY extensions/gubbins/gubbins-testing/ extensions/gubbins/gubbins-testing/
2830
COPY extensions/gubbins/pyproject.toml extensions/gubbins/

diracx-core/src/diracx/core/extensions.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class DiracEntryPoint(StrEnum):
3030
MIN_CLIENT_VERSION = "diracx.min_client_version"
3131
RESOURCES = "diracx.resources"
3232
SERVICES = "diracx.services"
33+
LOCK_OBJECT_TYPES = "diracx.lock_object_types"
3334

3435

3536
@cached(cache=LRUCache(maxsize=1))

diracx-db/src/diracx/db/__main__.py

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,28 @@
99

1010
def parse_args():
1111
parser = argparse.ArgumentParser()
12-
subparsers = parser.add_subparsers(required=True)
12+
subparsers = parser.add_subparsers(required=True, dest="command")
1313

14-
init_sql_parser = subparsers.add_parser(
15-
"init-sql", help="Initialise schema for SQL databases"
16-
)
17-
init_sql_parser.set_defaults(func=init_sql)
14+
subparsers.add_parser("init-sql", help="Initialise schema for SQL databases")
15+
subparsers.add_parser("init-os", help="Initialise schema for OpenSearch databases")
1816

19-
init_os_parser = subparsers.add_parser(
20-
"init-os", help="Initialise schema for OpenSearch databases"
17+
local_urls_parser = subparsers.add_parser(
18+
"generate-local-urls",
19+
help="Print shell exports for all registered DB URLs using sqlite",
20+
)
21+
local_urls_parser.add_argument(
22+
"tmp_dir", help="Temporary directory for database files"
2123
)
22-
init_os_parser.set_defaults(func=init_os)
2324

2425
args = parser.parse_args()
2526
logger.setLevel(logging.INFO)
26-
asyncio.run(args.func())
27+
28+
if args.command == "init-sql":
29+
asyncio.run(init_sql())
30+
elif args.command == "init-os":
31+
asyncio.run(init_os())
32+
elif args.command == "generate-local-urls":
33+
generate_local_urls(args.tmp_dir)
2734

2835

2936
async def init_sql():
@@ -53,5 +60,35 @@ async def init_os():
5360
await db.create_index_template()
5461

5562

63+
def generate_local_urls(tmp_dir: str) -> None:
64+
"""Print shell export statements for all registered DB URLs using sqlite.
65+
66+
Intended for use with eval in shell scripts::
67+
68+
eval "$(python -m diracx.db generate-local-urls /tmp/dir)"
69+
"""
70+
import json
71+
72+
from diracx.core.extensions import DiracEntryPoint, select_from_extension
73+
74+
seen: set[str] = set()
75+
for ep in select_from_extension(group=DiracEntryPoint.SQL_DB):
76+
if ep.name in seen:
77+
continue
78+
seen.add(ep.name)
79+
url = f"sqlite+aiosqlite:///{tmp_dir}/{ep.name.lower()}.db"
80+
print(f'export DIRACX_DB_URL_{ep.name.upper()}="{url}"')
81+
82+
seen.clear()
83+
for ep in select_from_extension(group=DiracEntryPoint.OS_DB):
84+
if ep.name in seen:
85+
continue
86+
seen.add(ep.name)
87+
v = json.dumps(
88+
{"sqlalchemy_dsn": f"sqlite+aiosqlite:///{tmp_dir}/{ep.name.lower()}.db"}
89+
)
90+
print(f"export DIRACX_OS_DB_{ep.name.upper()}='{v}'")
91+
92+
5693
if __name__ == "__main__":
5794
parse_args()

diracx-routers/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies = [
1717
"diracx-core",
1818
"diracx-logic",
1919
"diracx-db",
20+
"diracx-tasks",
2021
"python-dotenv", # TODO: We might not need this
2122
"python-multipart",
2223
"fastapi>=0.121.0",
Lines changed: 5 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,7 @@
1-
from __future__ import annotations
2-
3-
__all__ = (
4-
"Config",
5-
"AuthDB",
6-
"JobDB",
7-
"JobLoggingDB",
8-
"SandboxMetadataDB",
9-
"TaskQueueDB",
10-
"PilotAgentsDB",
11-
"add_settings_annotation",
12-
"AvailableSecurityProperties",
13-
)
14-
15-
from functools import partial
16-
from typing import Annotated, TypeVar
17-
18-
from fastapi import Depends
19-
20-
from diracx.core.config import Config as _Config
21-
from diracx.core.config import ConfigSource
22-
from diracx.core.properties import SecurityProperty
23-
from diracx.core.settings import AuthSettings as _AuthSettings
24-
from diracx.core.settings import DevelopmentSettings as _DevelopmentSettings
25-
from diracx.core.settings import SandboxStoreSettings as _SandboxStoreSettings
26-
from diracx.db.os import JobParametersDB as _JobParametersDB
27-
from diracx.db.sql import AuthDB as _AuthDB
28-
from diracx.db.sql import JobDB as _JobDB
29-
from diracx.db.sql import JobLoggingDB as _JobLoggingDB
30-
from diracx.db.sql import PilotAgentsDB as _PilotAgentsDB
31-
from diracx.db.sql import SandboxMetadataDB as _SandboxMetadataDB
32-
from diracx.db.sql import TaskQueueDB as _TaskQueueDB
33-
34-
T = TypeVar("T")
35-
36-
# Use scope="function" to ensure DB commits happen before sending HTTP responses
37-
# This prevents race conditions when DIRAC immediately queries data after DiracX writes it
38-
DBDepends = partial(Depends, scope="function")
1+
"""Router DI types — re-exported from the canonical definitions in diracx-tasks."""
392

3+
from __future__ import annotations
404

41-
def add_settings_annotation(cls: T) -> T:
42-
"""Add a `Depends` annotation to a class that has a `create` classmethod."""
43-
return Annotated[cls, Depends(cls.create)] # type: ignore
44-
45-
46-
# Databases
47-
AuthDB = Annotated[_AuthDB, DBDepends(_AuthDB.transaction)]
48-
JobDB = Annotated[_JobDB, DBDepends(_JobDB.transaction)]
49-
JobLoggingDB = Annotated[_JobLoggingDB, DBDepends(_JobLoggingDB.transaction)]
50-
PilotAgentsDB = Annotated[_PilotAgentsDB, DBDepends(_PilotAgentsDB.transaction)]
51-
SandboxMetadataDB = Annotated[
52-
_SandboxMetadataDB, DBDepends(_SandboxMetadataDB.transaction)
53-
]
54-
TaskQueueDB = Annotated[_TaskQueueDB, DBDepends(_TaskQueueDB.transaction)]
55-
56-
# Opensearch databases
57-
JobParametersDB = Annotated[_JobParametersDB, DBDepends(_JobParametersDB.session)]
58-
59-
60-
# Miscellaneous
61-
Config = Annotated[_Config, Depends(ConfigSource.create)]
62-
AvailableSecurityProperties = Annotated[
63-
set[SecurityProperty], Depends(SecurityProperty.available_properties)
64-
]
65-
66-
AuthSettings = Annotated[_AuthSettings, Depends(_AuthSettings.create)]
67-
DevelopmentSettings = Annotated[
68-
_DevelopmentSettings, Depends(_DevelopmentSettings.create)
69-
]
70-
SandboxStoreSettings = Annotated[
71-
_SandboxStoreSettings, Depends(_SandboxStoreSettings.create)
72-
]
5+
# Re-export everything from the canonical location
6+
from diracx.tasks.plumbing.depends import * # noqa: F401, F403
7+
from diracx.tasks.plumbing.depends import __all__ # noqa: F401

diracx-tasks/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# diracx-tasks
2+
3+
Asynchronous task system for DiracX.

diracx-tasks/pyproject.toml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
[project]
2+
name = "diracx-tasks"
3+
description = "Asynchronous task system for DiracX"
4+
readme = "README.md"
5+
requires-python = ">=3.11"
6+
keywords = []
7+
license = {text = "GPL-3.0-only"}
8+
classifiers = [
9+
"Intended Audience :: Science/Research",
10+
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
11+
"Programming Language :: Python :: 3",
12+
"Topic :: Scientific/Engineering",
13+
"Topic :: System :: Distributed Computing",
14+
]
15+
dependencies = [
16+
"diracx-core",
17+
"diracx-db",
18+
"diracx-logic",
19+
"fastapi>=0.121.0",
20+
"redis[hiredis]",
21+
"msgpack",
22+
"opentelemetry-api",
23+
"python-dateutil",
24+
"croniter",
25+
"pydantic >=2.10",
26+
]
27+
dynamic = ["version"]
28+
29+
[project.optional-dependencies]
30+
testing = ["diracx-testing", "fakeredis"]
31+
32+
[project.scripts]
33+
diracx-task-run = "diracx.tasks.task_run:main"
34+
35+
[project.entry-points."diracx.dbs.sql"]
36+
TaskDB = "diracx.tasks.plumbing.persistence:TaskDB"
37+
38+
[build-system]
39+
requires = ["hatchling", "hatch-vcs"]
40+
build-backend = "hatchling.build"
41+
42+
[tool.hatch.version]
43+
source = "vcs"
44+
45+
[tool.hatch.version.raw-options]
46+
root = ".."
47+
48+
[tool.hatch.build.targets.sdist.force-include]
49+
"../LICENSE" = "LICENSE"
50+
51+
[tool.hatch.build.targets.wheel]
52+
packages = ["src/diracx"]
53+
54+
[tool.pytest.ini_options]
55+
testpaths = ["tests"]
56+
addopts = [
57+
"-v",
58+
"--cov=diracx.tasks", "--cov-report=term-missing",
59+
"-pdiracx.testing",
60+
"--import-mode=importlib",
61+
]
62+
asyncio_mode = "auto"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from __future__ import annotations

0 commit comments

Comments
 (0)