|
1 | | -import gc |
2 | | -import threading |
3 | | -from typing import Any, Generator |
| 1 | +import subprocess |
| 2 | +import sys |
4 | 3 |
|
5 | | -import pyarrow as pa |
| 4 | +import pytest |
6 | 5 |
|
7 | | -from cloudquery.sdk.scheduler import Scheduler, TableResolver |
| 6 | +from cloudquery.sdk.scheduler import Scheduler |
8 | 7 | from cloudquery.sdk.scheduler.scheduler import QUEUE_PER_WORKER |
9 | | -from cloudquery.sdk.schema import Column |
10 | | -from cloudquery.sdk.schema.table import Table |
11 | | - |
12 | | - |
13 | | -class ParentResolver(TableResolver): |
14 | | - def __init__(self) -> None: |
15 | | - super().__init__( |
16 | | - Table("parent_table", [Column("id", pa.int64())]), |
17 | | - child_resolvers=[ChildResolver()], |
18 | | - ) |
19 | | - |
20 | | - def resolve(self, client, parent_resource) -> Generator[Any, None, None]: |
21 | | - for i in range(3): |
22 | | - yield {"id": i} |
23 | | - |
24 | | - |
25 | | -class ChildResolver(TableResolver): |
26 | | - def __init__(self) -> None: |
27 | | - super().__init__(Table("child_table", [Column("id", pa.int64())])) |
28 | | - |
29 | | - def resolve(self, client, parent_resource) -> Generator[Any, None, None]: |
30 | | - yield {"id": 1} |
31 | | - |
32 | | - |
33 | | -class QueueSizeTestClient: |
34 | | - def id(self): |
35 | | - return "test_client" |
36 | 8 |
|
37 | 9 |
|
38 | 10 | def test_default_queue_size_is_used_for_all_depths(): |
39 | 11 | concurrency = 10 |
40 | 12 | s = Scheduler(concurrency, max_depth=3) |
41 | 13 | expected_size = concurrency * QUEUE_PER_WORKER |
42 | 14 | for pool in s._pools: |
43 | | - assert pool._work_queue.maxsize == expected_size |
| 15 | + assert pool.maxsize == expected_size |
44 | 16 | expected_size = expected_size // 2 if expected_size > 1 else 1 |
45 | 17 | s.shutdown() |
46 | 18 |
|
47 | 19 |
|
48 | | -def test_dropping_used_scheduler_does_not_block(): |
49 | | - holder = {} |
50 | | - for _ in range(10): |
51 | | - holder["scheduler"] = Scheduler(10) |
52 | | - list(holder["scheduler"].sync(QueueSizeTestClient(), [ParentResolver()])) |
53 | | - |
54 | | - def replace_scheduler(): |
55 | | - holder["scheduler"] = Scheduler(10) |
56 | | - gc.collect() |
57 | | - |
58 | | - thread = threading.Thread(target=replace_scheduler, daemon=True) |
59 | | - thread.start() |
60 | | - thread.join(timeout=5) |
61 | | - assert not thread.is_alive() |
| 20 | +@pytest.mark.parametrize( |
| 21 | + "scenario", |
| 22 | + [ |
| 23 | + "drop_used_scheduler", |
| 24 | + "drop_used_scheduler_with_small_queue", |
| 25 | + "more_clients_than_top_level_queue", |
| 26 | + "grandchildren_with_small_queue", |
| 27 | + ], |
| 28 | +) |
| 29 | +def test_scenario_does_not_hang(scenario): |
| 30 | + result = subprocess.run( |
| 31 | + [sys.executable, "-m", "tests.scheduler.queue_size_scenarios", scenario], |
| 32 | + capture_output=True, |
| 33 | + text=True, |
| 34 | + timeout=60, |
| 35 | + ) |
| 36 | + assert result.returncode == 0, result.stderr |
0 commit comments