amsdal-glue 0.0.1__tar.gz
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.
- amsdal_glue-0.0.1/.gitignore +16 -0
- amsdal_glue-0.0.1/LICENSE.txt +3 -0
- amsdal_glue-0.0.1/PKG-INFO +43 -0
- amsdal_glue-0.0.1/README.md +18 -0
- amsdal_glue-0.0.1/pyproject.toml +202 -0
- amsdal_glue-0.0.1/src/amsdal_glue/__about__.py +1 -0
- amsdal_glue-0.0.1/src/amsdal_glue/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/planner/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/planner/data_planner.py +33 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/planner/lock_planner.py +25 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/planner/schema_planner.py +50 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/planner/transaction_planner.py +19 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/tasks/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/tasks/data_mutation_tasks.py +23 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/tasks/lock_tasks.py +30 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/tasks/schema_mutation_tasks.py +63 -0
- amsdal_glue-0.0.1/src/amsdal_glue/commands/tasks/transaction_tasks.py +30 -0
- amsdal_glue-0.0.1/src/amsdal_glue/connections/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/connections/connection_pool.py +66 -0
- amsdal_glue-0.0.1/src/amsdal_glue/initialize.py +61 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/executors/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/executors/palars_final_query_executor.py +257 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/planner/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/planner/data_query_planner.py +121 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/planner/schema_query_planner.py +37 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/polars_operator_constructor.py +21 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/tasks/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/tasks/query_tasks.py +47 -0
- amsdal_glue-0.0.1/src/amsdal_glue/queries/tasks/schema_tasks.py +47 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/data_command.py +21 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/data_query.py +21 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/lock_command.py +19 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/schema_command.py +21 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/schema_query.py +21 -0
- amsdal_glue-0.0.1/src/amsdal_glue/services/transaction_command.py +19 -0
- amsdal_glue-0.0.1/src/amsdal_glue/task_executors/__init__.py +0 -0
- amsdal_glue-0.0.1/src/amsdal_glue/task_executors/parallel_thread_executor.py +35 -0
- amsdal_glue-0.0.1/src/amsdal_glue/task_executors/sequential_sync_executor.py +31 -0
- amsdal_glue-0.0.1/tests/__init__.py +0 -0
- amsdal_glue-0.0.1/tests/fixtures/__init__.py +0 -0
- amsdal_glue-0.0.1/tests/fixtures/customers.sqlite +0 -0
- amsdal_glue-0.0.1/tests/fixtures/shippings.sqlite +0 -0
- amsdal_glue-0.0.1/tests/test_data_command_execute.py +343 -0
- amsdal_glue-0.0.1/tests/test_data_command_services.py +102 -0
- amsdal_glue-0.0.1/tests/test_data_query_service.py +127 -0
- amsdal_glue-0.0.1/tests/test_lock_execute.py +138 -0
- amsdal_glue-0.0.1/tests/test_lock_service.py +66 -0
- amsdal_glue-0.0.1/tests/test_query_execute.py +440 -0
- amsdal_glue-0.0.1/tests/test_schema_command_execute.py +121 -0
- amsdal_glue-0.0.1/tests/test_schema_command_service.py +109 -0
- amsdal_glue-0.0.1/tests/test_schema_query_execute.py +73 -0
- amsdal_glue-0.0.1/tests/test_schema_query_service.py +67 -0
- amsdal_glue-0.0.1/tests/test_transaction_execute.py +200 -0
- amsdal_glue-0.0.1/tests/test_transaction_service.py +62 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
2
|
+
|
|
3
|
+
The software contains third-party components that are distributed under the following license: https://github.com/pola-rs/polars/blob/main/LICENSE.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: amsdal-glue
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: AMSDAL Glue is a Python interface providing high-level abstraction for interacting with multiple databases simultaneously, simplifying the development and maintenance process.
|
|
5
|
+
Project-URL: Documentation, https://pypi.org/project/amsdal_glue/#readme
|
|
6
|
+
Project-URL: Issues, https://pypi.org/project/amsdal_glue/
|
|
7
|
+
Project-URL: Source, https://pypi.org/project/amsdal_glue/
|
|
8
|
+
License: Licensed under the Apache License, Version 2.0 (the "License"). You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
|
9
|
+
|
|
10
|
+
The software contains third-party components that are distributed under the following license: https://github.com/pola-rs/polars/blob/main/LICENSE.
|
|
11
|
+
License-File: LICENSE.txt
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Programming Language :: Python
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: amsdal-glue-core==0.0.*
|
|
21
|
+
Requires-Dist: polars>=0.20
|
|
22
|
+
Provides-Extra: connections
|
|
23
|
+
Requires-Dist: amsdal-glue-connections==0.0.*; extra == 'connections'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# amsdal-glue
|
|
27
|
+
|
|
28
|
+
AMSDAL Glue is a Python interface providing high-level abstraction for interacting with multiple databases simultaneously, simplifying the development and maintenance process.
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/amsdal-glue)
|
|
31
|
+
[](https://pypi.org/project/amsdal-glue)
|
|
32
|
+
|
|
33
|
+
-----
|
|
34
|
+
|
|
35
|
+
## Table of Contents
|
|
36
|
+
|
|
37
|
+
- [Installation](#installation)
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```console
|
|
42
|
+
pip install amsdal-glue
|
|
43
|
+
```
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# amsdal-glue
|
|
2
|
+
|
|
3
|
+
AMSDAL Glue is a Python interface providing high-level abstraction for interacting with multiple databases simultaneously, simplifying the development and maintenance process.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/amsdal-glue)
|
|
6
|
+
[](https://pypi.org/project/amsdal-glue)
|
|
7
|
+
|
|
8
|
+
-----
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Installation](#installation)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```console
|
|
17
|
+
pip install amsdal-glue
|
|
18
|
+
```
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[tool.hatch.build.targets.wheel]
|
|
6
|
+
packages = ["src/amsdal_glue"]
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "amsdal-glue"
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
description = 'AMSDAL Glue is a Python interface providing high-level abstraction for interacting with multiple databases simultaneously, simplifying the development and maintenance process.'
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.10"
|
|
14
|
+
license = { file = "LICENSE.txt" }
|
|
15
|
+
keywords = []
|
|
16
|
+
authors = []
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Programming Language :: Python",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: Implementation :: CPython",
|
|
24
|
+
"Programming Language :: Python :: Implementation :: PyPy",
|
|
25
|
+
]
|
|
26
|
+
dependencies = [
|
|
27
|
+
"amsdal_glue_core==0.0.*",
|
|
28
|
+
"polars>=0.20"
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.optional-dependencies]
|
|
32
|
+
connections = [
|
|
33
|
+
"amsdal-glue-connections==0.0.*",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Documentation = "https://pypi.org/project/amsdal_glue/#readme"
|
|
38
|
+
Issues = "https://pypi.org/project/amsdal_glue/"
|
|
39
|
+
Source = "https://pypi.org/project/amsdal_glue/"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.version]
|
|
42
|
+
path = "src/amsdal_glue/__about__.py"
|
|
43
|
+
|
|
44
|
+
[tool.hatch.envs.default]
|
|
45
|
+
path = "../../.venv/glue"
|
|
46
|
+
extra-dependencies = [
|
|
47
|
+
"mypy>=1.0.0",
|
|
48
|
+
"coverage[toml]>=7.4",
|
|
49
|
+
"pytest>=8.2.1",
|
|
50
|
+
"pytest-mock>=3.14.0",
|
|
51
|
+
"ruff>=0.4.5",
|
|
52
|
+
]
|
|
53
|
+
pre-install-commands = [
|
|
54
|
+
"pip install -e ../core/",
|
|
55
|
+
"pip install -e ../connections/",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.envs.default.scripts]
|
|
59
|
+
test = "pytest {args:tests}"
|
|
60
|
+
test-cov = "coverage run -m pytest {args:tests}"
|
|
61
|
+
cov-report = [
|
|
62
|
+
"- coverage combine",
|
|
63
|
+
"coverage report",
|
|
64
|
+
]
|
|
65
|
+
cov = [
|
|
66
|
+
"test-cov",
|
|
67
|
+
"cov-report",
|
|
68
|
+
]
|
|
69
|
+
typing = "mypy --install-types --non-interactive {args:src/amsdal_glue tests}"
|
|
70
|
+
style = [
|
|
71
|
+
"ruff check {args:.}",
|
|
72
|
+
"ruff format --preview {args:.}",
|
|
73
|
+
]
|
|
74
|
+
fmt = [
|
|
75
|
+
"ruff format {args:.}",
|
|
76
|
+
"ruff check --fix {args:.}",
|
|
77
|
+
"style",
|
|
78
|
+
]
|
|
79
|
+
all = [
|
|
80
|
+
"style",
|
|
81
|
+
"typing",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
[tool.ruff]
|
|
85
|
+
target-version = "py310"
|
|
86
|
+
line-length = 120
|
|
87
|
+
|
|
88
|
+
[tool.ruff.format]
|
|
89
|
+
quote-style = "single"
|
|
90
|
+
indent-style = "space"
|
|
91
|
+
docstring-code-format = true
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
select = [
|
|
95
|
+
"A",
|
|
96
|
+
"ARG",
|
|
97
|
+
"ASYNC",
|
|
98
|
+
"B",
|
|
99
|
+
"BLE",
|
|
100
|
+
"C",
|
|
101
|
+
"C4",
|
|
102
|
+
"C90",
|
|
103
|
+
"COM",
|
|
104
|
+
"DJ",
|
|
105
|
+
"DTZ",
|
|
106
|
+
"E",
|
|
107
|
+
"EM",
|
|
108
|
+
"ERA",
|
|
109
|
+
"F",
|
|
110
|
+
"FBT",
|
|
111
|
+
"FIX",
|
|
112
|
+
"FLY",
|
|
113
|
+
"FURB",
|
|
114
|
+
"G",
|
|
115
|
+
"I",
|
|
116
|
+
"ICN",
|
|
117
|
+
"INT",
|
|
118
|
+
"ISC",
|
|
119
|
+
"LOG",
|
|
120
|
+
"N",
|
|
121
|
+
"PERF",
|
|
122
|
+
"PIE",
|
|
123
|
+
"PL",
|
|
124
|
+
"PTH",
|
|
125
|
+
"PYI",
|
|
126
|
+
"Q",
|
|
127
|
+
"RET",
|
|
128
|
+
"RSE",
|
|
129
|
+
"RUF",
|
|
130
|
+
"S",
|
|
131
|
+
"SIM",
|
|
132
|
+
"SLF",
|
|
133
|
+
"SLOT",
|
|
134
|
+
"T",
|
|
135
|
+
"T10",
|
|
136
|
+
"T20",
|
|
137
|
+
"TCH",
|
|
138
|
+
"TD",
|
|
139
|
+
"TID",
|
|
140
|
+
"TRY",
|
|
141
|
+
"UP",
|
|
142
|
+
"W",
|
|
143
|
+
"YTT",
|
|
144
|
+
]
|
|
145
|
+
ignore = [
|
|
146
|
+
"COM812",
|
|
147
|
+
"ISC001",
|
|
148
|
+
]
|
|
149
|
+
unfixable = [
|
|
150
|
+
# Don't touch unused imports
|
|
151
|
+
"F401",
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
[tool.ruff.lint.isort]
|
|
155
|
+
force-single-line = true
|
|
156
|
+
order-by-type = false
|
|
157
|
+
|
|
158
|
+
[tool.ruff.lint.flake8-tidy-imports]
|
|
159
|
+
ban-relative-imports = "all"
|
|
160
|
+
|
|
161
|
+
[tool.ruff.lint.per-file-ignores]
|
|
162
|
+
# Tests can use magic values, assertions, and relative imports
|
|
163
|
+
"tests/**.py" = ["PLR2004", "S101", "TID252"]
|
|
164
|
+
|
|
165
|
+
[tool.ruff.lint.flake8-bandit]
|
|
166
|
+
check-typed-exception = true
|
|
167
|
+
|
|
168
|
+
[tool.ruff.lint.flake8-quotes]
|
|
169
|
+
inline-quotes = "single"
|
|
170
|
+
|
|
171
|
+
[tool.ruff.lint.mccabe]
|
|
172
|
+
# Flag errors (`C901`) whenever the complexity level exceeds 10.
|
|
173
|
+
max-complexity = 10
|
|
174
|
+
|
|
175
|
+
[tool.ruff.lint.pylint]
|
|
176
|
+
max-args = 6
|
|
177
|
+
|
|
178
|
+
[tool.ruff.lint.pydocstyle]
|
|
179
|
+
convention = "google"
|
|
180
|
+
|
|
181
|
+
[tool.mypy]
|
|
182
|
+
ignore_missing_imports = true
|
|
183
|
+
check_untyped_defs = true
|
|
184
|
+
|
|
185
|
+
[tool.coverage.run]
|
|
186
|
+
source_pkgs = ["amsdal_glue", "tests"]
|
|
187
|
+
branch = true
|
|
188
|
+
parallel = true
|
|
189
|
+
omit = [
|
|
190
|
+
"src/amsdal_glue/__about__.py",
|
|
191
|
+
]
|
|
192
|
+
|
|
193
|
+
[tool.coverage.paths]
|
|
194
|
+
amsdal_glue = ["src/amsdal_glue"]
|
|
195
|
+
tests = ["tests"]
|
|
196
|
+
|
|
197
|
+
[tool.coverage.report]
|
|
198
|
+
exclude_lines = [
|
|
199
|
+
"no cov",
|
|
200
|
+
"if __name__ == .__main__.:",
|
|
201
|
+
"if TYPE_CHECKING:",
|
|
202
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.0.1'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from amsdal_glue_core.commands.mutation_nodes import DataMutationNode
|
|
4
|
+
from amsdal_glue_core.commands.planner.data_command_planner import DataCommandPlanner
|
|
5
|
+
from amsdal_glue_core.common.helpers.resolve_connection import resolve_connection
|
|
6
|
+
from amsdal_glue_core.common.operations.commands import DataCommand
|
|
7
|
+
from amsdal_glue_core.common.workflows.chain import ChainTask
|
|
8
|
+
|
|
9
|
+
from amsdal_glue.commands.tasks.data_mutation_tasks import DataMutationTask
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from amsdal_glue_core.common.workflows.task import Task
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DefaultDataCommandPlanner(DataCommandPlanner):
|
|
16
|
+
def plan_data_command(self, command: DataCommand) -> ChainTask:
|
|
17
|
+
task_batches: list[Task] = []
|
|
18
|
+
prev_connection = None
|
|
19
|
+
|
|
20
|
+
for mutation in command.mutations:
|
|
21
|
+
connection = resolve_connection(mutation.schema, command.transaction_id)
|
|
22
|
+
|
|
23
|
+
if connection != prev_connection:
|
|
24
|
+
task_batches.append(
|
|
25
|
+
DataMutationTask(
|
|
26
|
+
data_mutation_node=DataMutationNode(mutations=[]),
|
|
27
|
+
)
|
|
28
|
+
)
|
|
29
|
+
prev_connection = connection
|
|
30
|
+
|
|
31
|
+
task_batches[-1].item.mutations.append(mutation)
|
|
32
|
+
|
|
33
|
+
return ChainTask(tasks=task_batches)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from amsdal_glue_core.commands.lock_command_node import ExecutionLockCommand
|
|
2
|
+
from amsdal_glue_core.commands.planner.lock_command_planner import LockCommandPlanner
|
|
3
|
+
from amsdal_glue_core.common.operations.commands import LockCommand
|
|
4
|
+
from amsdal_glue_core.common.workflows.chain import ChainTask
|
|
5
|
+
|
|
6
|
+
from amsdal_glue.commands.tasks.lock_tasks import LockCommandTask
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DefaultLockCommandPlanner(LockCommandPlanner):
|
|
10
|
+
def plan_lock(self, command: LockCommand) -> ChainTask:
|
|
11
|
+
return ChainTask(
|
|
12
|
+
tasks=[
|
|
13
|
+
LockCommandTask(
|
|
14
|
+
lock_command=ExecutionLockCommand(
|
|
15
|
+
lock_id=command.lock_id,
|
|
16
|
+
transaction_id=command.transaction_id,
|
|
17
|
+
action=command.action,
|
|
18
|
+
mode=command.mode,
|
|
19
|
+
parameter=command.parameter,
|
|
20
|
+
locked_object=lock_object,
|
|
21
|
+
)
|
|
22
|
+
)
|
|
23
|
+
for lock_object in command.locked_objects
|
|
24
|
+
]
|
|
25
|
+
)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
|
|
3
|
+
from amsdal_glue_core.commands.mutation_nodes import SchemaCommandNode
|
|
4
|
+
from amsdal_glue_core.commands.planner.schema_command_planner import SchemaCommandPlanner
|
|
5
|
+
from amsdal_glue_core.common.operations.commands import SchemaCommand
|
|
6
|
+
from amsdal_glue_core.common.services.managers.connection import ConnectionManager
|
|
7
|
+
from amsdal_glue_core.common.workflows.chain import ChainTask
|
|
8
|
+
|
|
9
|
+
from amsdal_glue.commands.tasks.schema_mutation_tasks import FinalSchemaCommandTask
|
|
10
|
+
from amsdal_glue.commands.tasks.schema_mutation_tasks import SchemaCommandTask
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DefaultSchemaCommandPlanner(SchemaCommandPlanner):
|
|
14
|
+
def plan_schema_command(self, command: SchemaCommand) -> ChainTask:
|
|
15
|
+
from amsdal_glue_core.containers import Container
|
|
16
|
+
|
|
17
|
+
mutations_per_connection = defaultdict(list)
|
|
18
|
+
connection_manager = Container.managers.get(ConnectionManager)
|
|
19
|
+
|
|
20
|
+
for mutation in command.mutations:
|
|
21
|
+
_schema_name = mutation.get_schema_name()
|
|
22
|
+
_connection = connection_manager.get_connection_pool(_schema_name)
|
|
23
|
+
mutations_per_connection[_connection].append(mutation)
|
|
24
|
+
|
|
25
|
+
if len(mutations_per_connection) > 1:
|
|
26
|
+
return ChainTask(
|
|
27
|
+
tasks=[
|
|
28
|
+
SchemaCommandTask(
|
|
29
|
+
command_node=SchemaCommandNode(command=command),
|
|
30
|
+
),
|
|
31
|
+
],
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
_tasks = [
|
|
35
|
+
SchemaCommandTask(
|
|
36
|
+
command_node=SchemaCommandNode(
|
|
37
|
+
command=SchemaCommand(
|
|
38
|
+
lock_id=command.lock_id,
|
|
39
|
+
transaction_id=command.transaction_id,
|
|
40
|
+
mutations=mutations,
|
|
41
|
+
),
|
|
42
|
+
),
|
|
43
|
+
)
|
|
44
|
+
for mutations in mutations_per_connection.values()
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
return ChainTask(
|
|
48
|
+
tasks=_tasks, # type: ignore[arg-type]
|
|
49
|
+
final_task=FinalSchemaCommandTask(tasks=_tasks),
|
|
50
|
+
)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from amsdal_glue_core.commands.planner.transaction_command_planner import TransactionCommandPlanner
|
|
2
|
+
from amsdal_glue_core.commands.transaction_node import ExecutionTransactionCommandNode
|
|
3
|
+
from amsdal_glue_core.common.operations.commands import TransactionCommand
|
|
4
|
+
from amsdal_glue_core.common.workflows.chain import ChainTask
|
|
5
|
+
|
|
6
|
+
from amsdal_glue.commands.tasks.transaction_tasks import TransactionCommandTask
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DefaultTransactionCommandPlanner(TransactionCommandPlanner):
|
|
10
|
+
def plan_transaction(self, command: TransactionCommand) -> ChainTask:
|
|
11
|
+
return ChainTask(
|
|
12
|
+
tasks=[
|
|
13
|
+
TransactionCommandTask(
|
|
14
|
+
transaction_command=ExecutionTransactionCommandNode(
|
|
15
|
+
command=command,
|
|
16
|
+
),
|
|
17
|
+
),
|
|
18
|
+
],
|
|
19
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from amsdal_glue_core.commands.executors.data_command_executor import DataCommandNodeExecutor
|
|
5
|
+
from amsdal_glue_core.commands.mutation_nodes import DataMutationNode
|
|
6
|
+
from amsdal_glue_core.common.workflows.task import Task
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(kw_only=True)
|
|
10
|
+
class DataMutationTask(Task):
|
|
11
|
+
data_mutation_node: DataMutationNode
|
|
12
|
+
|
|
13
|
+
def execute(self, transaction_id: str | None, lock_id: str | None) -> None:
|
|
14
|
+
_query_executor = DataCommandNodeExecutor()
|
|
15
|
+
_query_executor.execute(self.data_mutation_node, transaction_id=transaction_id, lock_id=lock_id)
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def item(self) -> Any:
|
|
19
|
+
return self.data_mutation_node
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def result(self) -> Any:
|
|
23
|
+
return self.data_mutation_node.result
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from amsdal_glue_core.commands.lock_command_node import ExecutionLockCommand
|
|
5
|
+
from amsdal_glue_core.common.workflows.task import Task
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(kw_only=True)
|
|
9
|
+
class LockCommandTask(Task):
|
|
10
|
+
lock_command: ExecutionLockCommand
|
|
11
|
+
|
|
12
|
+
def execute(self, transaction_id: str | None, lock_id: str | None) -> None:
|
|
13
|
+
from amsdal_glue_core.commands.executors.lock_command_executor import LockCommandNodeExecutor
|
|
14
|
+
|
|
15
|
+
_command_executor = LockCommandNodeExecutor()
|
|
16
|
+
_command_executor.execute(self.lock_command, transaction_id=transaction_id, lock_id=lock_id)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def item(self) -> Any:
|
|
20
|
+
return self.lock_command
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def result(self) -> Any:
|
|
24
|
+
return self.lock_command.result
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
return f'LockCommandTask<{self.lock_command}>'
|
|
28
|
+
|
|
29
|
+
def __hash__(self) -> int:
|
|
30
|
+
return hash(id(self))
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from amsdal_glue_core.commands.executors.schema_command_executor import SchemaCommandNodeExecutor
|
|
5
|
+
from amsdal_glue_core.commands.mutation_nodes import SchemaCommandNode
|
|
6
|
+
from amsdal_glue_core.common.operations.commands import SchemaCommand
|
|
7
|
+
from amsdal_glue_core.common.workflows.task import Task
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(kw_only=True)
|
|
11
|
+
class SchemaCommandTask(Task):
|
|
12
|
+
command_node: SchemaCommandNode
|
|
13
|
+
|
|
14
|
+
def __post_init__(self) -> None:
|
|
15
|
+
self._executor = SchemaCommandNodeExecutor()
|
|
16
|
+
|
|
17
|
+
def execute(self, transaction_id: str | None, lock_id: str | None) -> None:
|
|
18
|
+
self._executor.execute(self.command_node, transaction_id=transaction_id, lock_id=lock_id)
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def item(self) -> Any:
|
|
22
|
+
return self.command_node.command
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def result(self) -> list[Any] | None:
|
|
26
|
+
return self.command_node.result
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(kw_only=True)
|
|
30
|
+
class FinalSchemaCommandTask(Task):
|
|
31
|
+
tasks: list[SchemaCommandTask]
|
|
32
|
+
|
|
33
|
+
def __post_init__(self) -> None:
|
|
34
|
+
self._result: SchemaCommandNode | None = None
|
|
35
|
+
|
|
36
|
+
def execute(self, transaction_id: str | None, lock_id: str | None) -> None: # noqa: ARG002
|
|
37
|
+
if not self.tasks:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
_command = SchemaCommand(
|
|
41
|
+
lock_id=self.tasks[0].item.lock_id,
|
|
42
|
+
transaction_id=self.tasks[0].item.transaction_id,
|
|
43
|
+
mutations=[],
|
|
44
|
+
)
|
|
45
|
+
_result = []
|
|
46
|
+
|
|
47
|
+
for task in self.tasks:
|
|
48
|
+
_command.mutations.extend(task.item.mutations)
|
|
49
|
+
_results = [task.result[i] if task.result else None for i in range(len(task.item.mutations))]
|
|
50
|
+
_result.extend(_results)
|
|
51
|
+
|
|
52
|
+
self._result = SchemaCommandNode(
|
|
53
|
+
command=_command,
|
|
54
|
+
result=_result,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def item(self) -> Any:
|
|
59
|
+
return [task.item for task in self.tasks]
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def result(self) -> list[Any] | None:
|
|
63
|
+
return self._result.result if self._result else None
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from amsdal_glue_core.commands.transaction_node import ExecutionTransactionCommandNode
|
|
5
|
+
from amsdal_glue_core.common.workflows.task import Task
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(kw_only=True)
|
|
9
|
+
class TransactionCommandTask(Task):
|
|
10
|
+
transaction_command: ExecutionTransactionCommandNode
|
|
11
|
+
|
|
12
|
+
def execute(self, transaction_id: str | None, lock_id: str | None) -> None:
|
|
13
|
+
from amsdal_glue_core.commands.executors.transaction_command_executor import TransactionNodeExecutor
|
|
14
|
+
|
|
15
|
+
_command_executor = TransactionNodeExecutor()
|
|
16
|
+
_command_executor.execute(self.transaction_command, transaction_id=transaction_id, lock_id=lock_id)
|
|
17
|
+
|
|
18
|
+
@property
|
|
19
|
+
def item(self) -> Any:
|
|
20
|
+
return self.transaction_command
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def result(self) -> Any:
|
|
24
|
+
return self.transaction_command.result
|
|
25
|
+
|
|
26
|
+
def __repr__(self) -> str:
|
|
27
|
+
return f'TransactionCommandTask<{self.transaction_command}>'
|
|
28
|
+
|
|
29
|
+
def __hash__(self) -> int:
|
|
30
|
+
return hash(id(self))
|
|
File without changes
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from datetime import timezone
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from amsdal_glue_core.common.interfaces.connection import ConnectionBase
|
|
8
|
+
from amsdal_glue_core.common.services.managers.connection import ConnectionPoolBase
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DefaultConnectionPool(ConnectionPoolBase):
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
connection_class: type[ConnectionBase],
|
|
15
|
+
*args: Any,
|
|
16
|
+
max_connections: int = 10,
|
|
17
|
+
expiration_time: int = 60,
|
|
18
|
+
**kwargs: Any,
|
|
19
|
+
) -> None:
|
|
20
|
+
super().__init__(connection_class, *args, **kwargs)
|
|
21
|
+
self.connections: dict[str | None, tuple[ConnectionBase, datetime]] = {}
|
|
22
|
+
self._max_connections = max_connections
|
|
23
|
+
self._expiration_time = expiration_time
|
|
24
|
+
|
|
25
|
+
def _now(self) -> datetime:
|
|
26
|
+
return datetime.now(tz=timezone.utc)
|
|
27
|
+
|
|
28
|
+
def _get_available_connection(self) -> ConnectionBase:
|
|
29
|
+
for transaction_id, (connection, last_used) in list(self.connections.items()):
|
|
30
|
+
if self._now() - last_used > timedelta(seconds=self._expiration_time):
|
|
31
|
+
with contextlib.suppress(Exception):
|
|
32
|
+
connection.rollback_transaction(transaction_id)
|
|
33
|
+
|
|
34
|
+
self.connections.pop(transaction_id)
|
|
35
|
+
|
|
36
|
+
return connection
|
|
37
|
+
|
|
38
|
+
if len(self.connections) >= self._max_connections:
|
|
39
|
+
msg = 'Max connections reached'
|
|
40
|
+
raise RuntimeError(msg)
|
|
41
|
+
|
|
42
|
+
connection = self._connection_class()
|
|
43
|
+
connection.connect(*self._connection_args, **self._connection_kwargs)
|
|
44
|
+
|
|
45
|
+
return connection
|
|
46
|
+
|
|
47
|
+
def get_connection(self, transaction_id: str | None = None) -> ConnectionBase:
|
|
48
|
+
if transaction_id in self.connections:
|
|
49
|
+
connection, _ = self.connections[transaction_id]
|
|
50
|
+
|
|
51
|
+
self.connections[transaction_id] = (connection, self._now())
|
|
52
|
+
|
|
53
|
+
return connection
|
|
54
|
+
|
|
55
|
+
connection = self._get_available_connection()
|
|
56
|
+
|
|
57
|
+
self.connections[transaction_id] = (connection, self._now())
|
|
58
|
+
|
|
59
|
+
return connection
|
|
60
|
+
|
|
61
|
+
def disconnect(self) -> None:
|
|
62
|
+
for connection, _ in self.connections.values():
|
|
63
|
+
if connection.is_connected:
|
|
64
|
+
connection.disconnect()
|
|
65
|
+
|
|
66
|
+
self.connections.clear()
|