dlt-utils-lib 1.2.6__tar.gz → 1.2.7__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.
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/dlt_autoloader_recovery_configuration.py +14 -1
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils_lib.egg-info/PKG-INFO +1 -1
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils_lib.egg-info/SOURCES.txt +1 -0
- dlt_utils_lib-1.2.7/tests/test_autoloader_recovery_configuration.py +68 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/__init__.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/dlt_metadata_receiver.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/dlt_transformations.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/main_cdc_utils.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils_lib.egg-info/dependency_links.txt +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils_lib.egg-info/requires.txt +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils_lib.egg-info/top_level.txt +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/setup.cfg +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/setup.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/tests/__init__.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/tests/conftest.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/tests/test_main_cdc_utils.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/tests/test_main_json_utils.py +0 -0
- {dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/tests/test_transformation_module.py +0 -0
{dlt_utils_lib-1.2.6 → dlt_utils_lib-1.2.7}/dlt_utils/dlt_autoloader_recovery_configuration.py
RENAMED
|
@@ -2,6 +2,8 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from databricks.sdk import WorkspaceClient
|
|
4
4
|
|
|
5
|
+
_RECOVERY_DECISION = None
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
def _needs_autoloader_recovery(pipeline_id: str) -> bool:
|
|
7
9
|
event = next(iter(WorkspaceClient().pipelines.list_pipeline_events(
|
|
@@ -18,9 +20,20 @@ def _needs_autoloader_recovery(pipeline_id: str) -> bool:
|
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
def resolve_autoloader_recovery(spark) -> bool:
|
|
23
|
+
# Cached for the lifetime of the driver process: the decision belongs to the
|
|
24
|
+
# update as a whole, but callers ask once per table while the pipeline graph
|
|
25
|
+
# is built. Uncached, a 94-table pipeline makes 94 Pipelines API calls,
|
|
26
|
+
# serialized on the driver, before a single stream starts.
|
|
27
|
+
global _RECOVERY_DECISION
|
|
28
|
+
if _RECOVERY_DECISION is None:
|
|
29
|
+
_RECOVERY_DECISION = _resolve_autoloader_recovery(spark)
|
|
30
|
+
return _RECOVERY_DECISION
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _resolve_autoloader_recovery(spark) -> bool:
|
|
21
34
|
method = spark.conf.get("autoloader_recovery_method", "AUTO").upper()
|
|
22
35
|
if method == "ON":
|
|
23
36
|
return True
|
|
24
37
|
if method == "AUTO":
|
|
25
38
|
return _needs_autoloader_recovery(spark.conf.get("pipelines.id"))
|
|
26
|
-
return False
|
|
39
|
+
return False
|
|
@@ -12,6 +12,7 @@ dlt_utils_lib.egg-info/requires.txt
|
|
|
12
12
|
dlt_utils_lib.egg-info/top_level.txt
|
|
13
13
|
tests/__init__.py
|
|
14
14
|
tests/conftest.py
|
|
15
|
+
tests/test_autoloader_recovery_configuration.py
|
|
15
16
|
tests/test_main_cdc_utils.py
|
|
16
17
|
tests/test_main_json_utils.py
|
|
17
18
|
tests/test_transformation_module.py
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
|
|
3
|
+
import dlt_utils.dlt_autoloader_recovery_configuration as recovery
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _SparkConfStub:
|
|
7
|
+
def __init__(self, values):
|
|
8
|
+
self._values = values
|
|
9
|
+
|
|
10
|
+
def get(self, key, default=None):
|
|
11
|
+
return self._values.get(key, default)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _SparkStub:
|
|
15
|
+
def __init__(self, **values):
|
|
16
|
+
self.conf = _SparkConfStub(values)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@pytest.fixture(autouse=True)
|
|
20
|
+
def reset_cached_decision():
|
|
21
|
+
recovery._RECOVERY_DECISION = None
|
|
22
|
+
yield
|
|
23
|
+
recovery._RECOVERY_DECISION = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_method_on_forces_recovery_without_calling_the_api(monkeypatch):
|
|
27
|
+
monkeypatch.setattr(recovery, "_needs_autoloader_recovery", _unexpected_api_call)
|
|
28
|
+
|
|
29
|
+
assert recovery.resolve_autoloader_recovery(
|
|
30
|
+
_SparkStub(autoloader_recovery_method="ON")
|
|
31
|
+
) is True
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_method_off_skips_recovery_without_calling_the_api(monkeypatch):
|
|
35
|
+
monkeypatch.setattr(recovery, "_needs_autoloader_recovery", _unexpected_api_call)
|
|
36
|
+
|
|
37
|
+
assert recovery.resolve_autoloader_recovery(
|
|
38
|
+
_SparkStub(autoloader_recovery_method="OFF")
|
|
39
|
+
) is False
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_method_auto_asks_the_pipelines_api(monkeypatch):
|
|
43
|
+
seen = []
|
|
44
|
+
monkeypatch.setattr(recovery, "_needs_autoloader_recovery",
|
|
45
|
+
lambda pipeline_id: seen.append(pipeline_id) or True)
|
|
46
|
+
|
|
47
|
+
spark = _SparkStub(autoloader_recovery_method="AUTO", **{"pipelines.id": "pipeline-1"})
|
|
48
|
+
|
|
49
|
+
assert recovery.resolve_autoloader_recovery(spark) is True
|
|
50
|
+
assert seen == ["pipeline-1"]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_decision_is_resolved_once_per_process(monkeypatch):
|
|
54
|
+
calls = []
|
|
55
|
+
monkeypatch.setattr(recovery, "_needs_autoloader_recovery",
|
|
56
|
+
lambda pipeline_id: calls.append(pipeline_id) or True)
|
|
57
|
+
|
|
58
|
+
spark = _SparkStub(autoloader_recovery_method="AUTO", **{"pipelines.id": "pipeline-1"})
|
|
59
|
+
|
|
60
|
+
# One call per table while the DLT graph is built; only the first may hit the API.
|
|
61
|
+
for _ in range(10):
|
|
62
|
+
assert recovery.resolve_autoloader_recovery(spark) is True
|
|
63
|
+
|
|
64
|
+
assert calls == ["pipeline-1"]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _unexpected_api_call(pipeline_id):
|
|
68
|
+
raise AssertionError(f"Pipelines API should not be called (pipeline_id={pipeline_id})")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|