|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | 5 | import threading |
6 | | -import time |
7 | 6 | from pathlib import Path |
8 | 7 | from textwrap import dedent |
9 | 8 | from typing import Any |
|
22 | 21 | run_concurrent_build, |
23 | 22 | ) |
24 | 23 |
|
| 24 | +_OVERLAP_TIMEOUT_SECONDS: float = 30.0 |
| 25 | + |
25 | 26 |
|
26 | 27 | class _TrackingDuckDbAdapter(DuckDbAdapter): |
27 | | - def __init__(self) -> None: |
| 28 | + def __init__(self, *, overlap_target: int) -> None: |
28 | 29 | self.track_delta_staging = False |
| 30 | + self.overlap_target = overlap_target |
| 31 | + self.overlap_wait_timed_out = False |
| 32 | + self._overlap_reached = threading.Event() |
| 33 | + self._serial_first_batch_models: set[str] = set() |
29 | 34 | self.active_delta_staging = 0 |
30 | 35 | self.max_active_delta_staging = 0 |
31 | 36 | self.active_delta_models: dict[str, int] = {} |
@@ -53,7 +58,15 @@ def _execute(self, *, connection: Any, sql: str) -> Any: |
53 | 58 | self.max_active_delta_models, |
54 | 59 | len(self.active_delta_models), |
55 | 60 | ) |
56 | | - time.sleep(0.05) |
| 61 | + if self.active_delta_staging >= self.overlap_target: |
| 62 | + self._overlap_reached.set() |
| 63 | + # Each model stages its first batch serially before fanning out. |
| 64 | + gated: bool = model_name in self._serial_first_batch_models |
| 65 | + self._serial_first_batch_models.add(model_name) |
| 66 | + if gated and not self._overlap_reached.wait(timeout=_OVERLAP_TIMEOUT_SECONDS): |
| 67 | + with self._tracking_lock: |
| 68 | + self.overlap_wait_timed_out = True |
| 69 | + self._overlap_reached.set() |
57 | 70 | try: |
58 | 71 | return super()._execute(connection=connection, sql=sql) |
59 | 72 | finally: |
@@ -167,7 +180,9 @@ def test_given_three_batch_ceiling_when_incremental_runs_then_batches_overlap( |
167 | 180 | destination.parent.mkdir(parents=True, exist_ok=True) |
168 | 181 | destination.write_text(contents, encoding="utf-8") |
169 | 182 |
|
170 | | - adapter: _TrackingDuckDbAdapter = _TrackingDuckDbAdapter() |
| 183 | + adapter: _TrackingDuckDbAdapter = _TrackingDuckDbAdapter( |
| 184 | + overlap_target=test_case.expected_max_active_batches |
| 185 | + ) |
171 | 186 | db_path: Path = tmp_path / "test.duckdb" |
172 | 187 | initial_project_files: dict[str, str] = { |
173 | 188 | path: contents.replace("batch_concurrency 3", "batch_concurrency 1") |
@@ -232,6 +247,10 @@ def test_given_three_batch_ceiling_when_incremental_runs_then_batches_overlap( |
232 | 247 | adapter=adapter, |
233 | 248 | ) |
234 | 249 |
|
| 250 | + assert not adapter.overlap_wait_timed_out, ( |
| 251 | + f"only {adapter.max_active_delta_staging} delta stagings were ever active together; " |
| 252 | + f"expected {test_case.expected_max_active_batches} within {_OVERLAP_TIMEOUT_SECONDS}s" |
| 253 | + ) |
235 | 254 | assert incremental_result.status == test_case.expected_status |
236 | 255 | assert adapter.max_active_delta_staging == test_case.expected_max_active_batches |
237 | 256 | assert adapter.max_active_delta_models == test_case.expected_max_active_models |
|
0 commit comments