Skip to content

Commit 6b116c3

Browse files
authored
refactor(meta-semaphore): handle error occurs during new-stream, lease-extend (#18695)
* refactor(meta-semaphore): handle error occurs during new-stream, lease-extend * fix: semaphore: that subscriber quits does not mean the permit is acquired * feat: semaphore subscriber send connectino-error to acquirer * feat: semaphore: keep retrying if to meta connection is lost
1 parent b20c33b commit 6b116c3

Some content is hidden

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

42 files changed

+2709
-106
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Databend Test Helper
2+
3+
Python library for managing Databend processes during testing.
4+
5+
## Installation
6+
7+
```bash
8+
pip install -e .
9+
```
10+
11+
## Usage
12+
13+
```python
14+
from databend_test_helper import DatabendMeta, DatabendQuery
15+
16+
# Start meta service (uses default config)
17+
meta = DatabendMeta()
18+
meta.start()
19+
20+
# Start query service (uses default config)
21+
query = DatabendQuery()
22+
query.start()
23+
24+
# Or use custom config
25+
meta = DatabendMeta(config_path="custom-meta.toml")
26+
query = DatabendQuery(config_path="custom-query.toml")
27+
28+
# Stop services
29+
query.stop()
30+
meta.stop()
31+
```
32+
33+
## Configuration
34+
35+
Default configs are included:
36+
- `databend_test_helper/configs/databend-meta.toml`
37+
- `databend_test_helper/configs/databend-query.toml`
38+
39+
The library automatically parses config files to determine ports for health checking.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Databend Test Helper
2+
3+
A Python library for starting and stopping Databend processes during testing.
4+
Provides utilities for managing databend-meta and databend-query instances.
5+
"""
6+
7+
__version__ = "0.1.0"
8+
9+
from .meta import DatabendMeta
10+
from .query import DatabendQuery
11+
from .progress import ProgressReporter
12+
from .meta_cluster import MetaCluster
13+
from .query_cluster import QueryCluster
14+
from .cluster import DatabendCluster
15+
from .args import MetaArgs, QueryArgs
16+
17+
__all__ = ["DatabendMeta", "DatabendQuery", "ProgressReporter", "MetaCluster", "QueryCluster", "DatabendCluster", "MetaArgs", "QueryArgs"]
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
"""CLI arguments classes for Databend processes."""
2+
3+
from dataclasses import dataclass
4+
from typing import Optional, List, Dict, Any
5+
import copy
6+
7+
from .utils import LogConfigHelper
8+
9+
10+
class ArgsUtils:
11+
"""Utility functions for Args classes."""
12+
13+
@staticmethod
14+
def clone_args(args_instance):
15+
"""Create a deep copy of an Args instance."""
16+
return copy.deepcopy(args_instance)
17+
18+
@staticmethod
19+
def merge_args(target_args, source_args):
20+
"""Merge source Args into target Args, replacing None values in-place."""
21+
for field_name, field_value in source_args.__dict__.items():
22+
if field_value is not None:
23+
setattr(target_args, field_name, field_value)
24+
return target_args
25+
26+
27+
@dataclass
28+
class MetaArgs:
29+
"""CLI arguments for databend-meta process."""
30+
31+
# Network configuration
32+
admin_api_address: Optional[str] = None
33+
grpc_api_address: Optional[str] = None
34+
grpc_api_advertise_host: Optional[str] = None
35+
36+
# Raft configuration
37+
raft_dir: Optional[str] = None
38+
raft_api_port: Optional[int] = None
39+
raft_listen_host: Optional[str] = None
40+
raft_advertise_host: Optional[str] = None
41+
42+
# Cluster configuration
43+
id: Optional[int] = None
44+
single: Optional[bool] = None
45+
join: Optional[List[str]] = None
46+
47+
# Log configuration
48+
log_level: Optional[str] = None
49+
log_dir: Optional[str] = None
50+
log_format: Optional[str] = None
51+
52+
def to_config_overrides(self) -> dict:
53+
"""Convert CLI args to config file overrides."""
54+
overrides = {}
55+
56+
# Top-level fields
57+
if self.admin_api_address is not None:
58+
overrides["admin_api_address"] = self.admin_api_address
59+
if self.grpc_api_address is not None:
60+
overrides["grpc_api_address"] = self.grpc_api_address
61+
if self.grpc_api_advertise_host is not None:
62+
overrides["grpc_api_advertise_host"] = self.grpc_api_advertise_host
63+
if self.single is not None:
64+
overrides["single"] = self.single
65+
if self.join is not None:
66+
overrides["join"] = self.join
67+
68+
# Raft config section
69+
raft_config = {}
70+
if self.id is not None:
71+
raft_config["id"] = self.id
72+
if self.raft_dir is not None:
73+
raft_config["raft_dir"] = self.raft_dir
74+
if self.raft_api_port is not None:
75+
raft_config["raft_api_port"] = self.raft_api_port
76+
if self.raft_listen_host is not None:
77+
raft_config["raft_listen_host"] = self.raft_listen_host
78+
if self.raft_advertise_host is not None:
79+
raft_config["raft_advertise_host"] = self.raft_advertise_host
80+
81+
if raft_config:
82+
overrides["raft_config"] = raft_config
83+
84+
# Log config section
85+
log_config = LogConfigHelper.build_log_config_overrides(self.log_level, self.log_dir, self.log_format)
86+
if log_config:
87+
overrides["log"] = log_config
88+
89+
return overrides
90+
91+
def to_cli_args(self) -> List[str]:
92+
"""Convert CLI args to command line arguments for databend-meta."""
93+
cli_args = []
94+
95+
if self.admin_api_address is not None:
96+
cli_args.extend(["--admin-api-address", self.admin_api_address])
97+
if self.grpc_api_address is not None:
98+
cli_args.extend(["--grpc-api-address", self.grpc_api_address])
99+
if self.grpc_api_advertise_host is not None:
100+
cli_args.extend(["--grpc-api-advertise-host", self.grpc_api_advertise_host])
101+
102+
if self.id is not None:
103+
cli_args.extend(["--id", str(self.id)])
104+
if self.raft_dir is not None:
105+
cli_args.extend(["--raft-dir", self.raft_dir])
106+
if self.raft_api_port is not None:
107+
cli_args.extend(["--raft-api-port", str(self.raft_api_port)])
108+
if self.raft_listen_host is not None:
109+
cli_args.extend(["--raft-listen-host", self.raft_listen_host])
110+
if self.raft_advertise_host is not None:
111+
cli_args.extend(["--raft-advertise-host", self.raft_advertise_host])
112+
113+
if self.single is not None:
114+
if self.single:
115+
cli_args.append("--single")
116+
if self.join is not None:
117+
for endpoint in self.join:
118+
cli_args.extend(["--join", endpoint])
119+
120+
if self.log_level is not None:
121+
cli_args.extend(["--log-level", self.log_level])
122+
if self.log_dir is not None:
123+
cli_args.extend(["--log-dir", self.log_dir])
124+
if self.log_format is not None:
125+
cli_args.extend(["--log-format", self.log_format])
126+
127+
return cli_args
128+
129+
def clone(self) -> 'MetaArgs':
130+
"""Create a deep copy of this MetaArgs instance."""
131+
return ArgsUtils.clone_args(self)
132+
133+
def merge(self, other: 'MetaArgs') -> 'MetaArgs':
134+
"""Merge another MetaArgs into this one, replacing None values in-place."""
135+
return ArgsUtils.merge_args(self, other)
136+
137+
138+
@dataclass
139+
class QueryArgs:
140+
"""CLI arguments for databend-query process."""
141+
142+
# API configuration
143+
admin_api_address: Optional[str] = None
144+
metric_api_address: Optional[str] = None
145+
http_handler_host: Optional[str] = None
146+
http_handler_port: Optional[int] = None
147+
mysql_handler_host: Optional[str] = None
148+
mysql_handler_port: Optional[int] = None
149+
clickhouse_http_handler_host: Optional[str] = None
150+
clickhouse_http_handler_port: Optional[int] = None
151+
flight_sql_handler_host: Optional[str] = None
152+
flight_sql_handler_port: Optional[int] = None
153+
154+
# Cluster configuration
155+
tenant_id: Optional[str] = None
156+
cluster_id: Optional[str] = None
157+
158+
# Query configuration
159+
max_active_sessions: Optional[int] = None
160+
table_engine_memory_enabled: Optional[bool] = None
161+
162+
# Meta configuration
163+
meta_endpoints: Optional[List[str]] = None
164+
meta_username: Optional[str] = None
165+
meta_password: Optional[str] = None
166+
167+
# Storage configuration
168+
storage_type: Optional[str] = None
169+
storage_fs_data_path: Optional[str] = None
170+
171+
# Log configuration
172+
log_level: Optional[str] = None
173+
log_dir: Optional[str] = None
174+
log_format: Optional[str] = None
175+
log_query_enabled: Optional[bool] = None
176+
177+
def to_config_overrides(self) -> dict:
178+
"""Convert CLI args to config file overrides."""
179+
overrides = {}
180+
181+
# Query config section
182+
query_config = {}
183+
if self.admin_api_address is not None:
184+
query_config["admin_api_address"] = self.admin_api_address
185+
if self.metric_api_address is not None:
186+
query_config["metric_api_address"] = self.metric_api_address
187+
if self.http_handler_host is not None:
188+
query_config["http_handler_host"] = self.http_handler_host
189+
if self.http_handler_port is not None:
190+
query_config["http_handler_port"] = self.http_handler_port
191+
if self.mysql_handler_host is not None:
192+
query_config["mysql_handler_host"] = self.mysql_handler_host
193+
if self.mysql_handler_port is not None:
194+
query_config["mysql_handler_port"] = self.mysql_handler_port
195+
if self.clickhouse_http_handler_host is not None:
196+
query_config["clickhouse_http_handler_host"] = self.clickhouse_http_handler_host
197+
if self.clickhouse_http_handler_port is not None:
198+
query_config["clickhouse_http_handler_port"] = self.clickhouse_http_handler_port
199+
if self.flight_sql_handler_host is not None:
200+
query_config["flight_sql_handler_host"] = self.flight_sql_handler_host
201+
if self.flight_sql_handler_port is not None:
202+
query_config["flight_sql_handler_port"] = self.flight_sql_handler_port
203+
if self.tenant_id is not None:
204+
query_config["tenant_id"] = self.tenant_id
205+
if self.cluster_id is not None:
206+
query_config["cluster_id"] = self.cluster_id
207+
if self.max_active_sessions is not None:
208+
query_config["max_active_sessions"] = self.max_active_sessions
209+
if self.table_engine_memory_enabled is not None:
210+
query_config["table_engine_memory_enabled"] = self.table_engine_memory_enabled
211+
212+
if query_config:
213+
overrides["query"] = query_config
214+
215+
# Meta config section
216+
meta_config = {}
217+
if self.meta_endpoints is not None:
218+
meta_config["endpoints"] = self.meta_endpoints
219+
if self.meta_username is not None:
220+
meta_config["username"] = self.meta_username
221+
if self.meta_password is not None:
222+
meta_config["password"] = self.meta_password
223+
224+
if meta_config:
225+
overrides["meta"] = meta_config
226+
227+
# Storage config section
228+
storage_config = {}
229+
if self.storage_type is not None:
230+
storage_config["type"] = self.storage_type
231+
232+
if self.storage_fs_data_path is not None:
233+
fs_config = {"data_path": self.storage_fs_data_path}
234+
storage_config["fs"] = fs_config
235+
236+
if storage_config:
237+
overrides["storage"] = storage_config
238+
239+
# Log config section
240+
log_config = LogConfigHelper.build_log_config_overrides(self.log_level, self.log_dir, self.log_format)
241+
if self.log_query_enabled is not None:
242+
log_config["query"] = {"on": self.log_query_enabled}
243+
244+
if log_config:
245+
overrides["log"] = log_config
246+
247+
return overrides
248+
def to_cli_args(self) -> List[str]:
249+
"""Convert CLI args to command line arguments for databend-query."""
250+
cli_args = []
251+
252+
if self.admin_api_address is not None:
253+
cli_args.extend(["--admin-api-address", self.admin_api_address])
254+
if self.metric_api_address is not None:
255+
cli_args.extend(["--metric-api-address", self.metric_api_address])
256+
257+
if self.http_handler_host is not None:
258+
cli_args.extend(["--http-handler-host", self.http_handler_host])
259+
if self.http_handler_port is not None:
260+
cli_args.extend(["--http-handler-port", str(self.http_handler_port)])
261+
if self.mysql_handler_host is not None:
262+
cli_args.extend(["--mysql-handler-host", self.mysql_handler_host])
263+
if self.mysql_handler_port is not None:
264+
cli_args.extend(["--mysql-handler-port", str(self.mysql_handler_port)])
265+
if self.clickhouse_http_handler_host is not None:
266+
cli_args.extend(["--clickhouse-http-handler-host", self.clickhouse_http_handler_host])
267+
if self.clickhouse_http_handler_port is not None:
268+
cli_args.extend(["--clickhouse-http-handler-port", str(self.clickhouse_http_handler_port)])
269+
if self.flight_sql_handler_host is not None:
270+
cli_args.extend(["--flight-sql-handler-host", self.flight_sql_handler_host])
271+
if self.flight_sql_handler_port is not None:
272+
cli_args.extend(["--flight-sql-handler-port", str(self.flight_sql_handler_port)])
273+
274+
if self.tenant_id is not None:
275+
cli_args.extend(["--tenant-id", self.tenant_id])
276+
if self.cluster_id is not None:
277+
cli_args.extend(["--cluster-id", self.cluster_id])
278+
if self.max_active_sessions is not None:
279+
cli_args.extend(["--max-active-sessions", str(self.max_active_sessions)])
280+
if self.table_engine_memory_enabled is not None:
281+
cli_args.extend(["--table-engine-memory-enabled", str(self.table_engine_memory_enabled).lower()])
282+
283+
if self.meta_endpoints is not None:
284+
for endpoint in self.meta_endpoints:
285+
cli_args.extend(["--meta-endpoint", endpoint])
286+
if self.meta_username is not None:
287+
cli_args.extend(["--meta-username", self.meta_username])
288+
if self.meta_password is not None:
289+
cli_args.extend(["--meta-password", self.meta_password])
290+
291+
if self.storage_type is not None:
292+
cli_args.extend(["--storage-type", self.storage_type])
293+
if self.storage_fs_data_path is not None:
294+
cli_args.extend(["--storage-fs-data-path", self.storage_fs_data_path])
295+
296+
if self.log_level is not None:
297+
cli_args.extend(["--log-level", self.log_level])
298+
if self.log_dir is not None:
299+
cli_args.extend(["--log-dir", self.log_dir])
300+
if self.log_format is not None:
301+
cli_args.extend(["--log-format", self.log_format])
302+
if self.log_query_enabled is not None:
303+
cli_args.extend(["--log-query-enabled", str(self.log_query_enabled).lower()])
304+
305+
return cli_args
306+
307+
def clone(self) -> 'QueryArgs':
308+
"""Create a deep copy of this QueryArgs instance."""
309+
return ArgsUtils.clone_args(self)
310+
311+
def merge(self, other: 'QueryArgs') -> 'QueryArgs':
312+
"""Merge another QueryArgs into this one, replacing None values in-place."""
313+
return ArgsUtils.merge_args(self, other)

0 commit comments

Comments
 (0)