Skip to content

Commit 84ee747

Browse files
authored
Merge 86ce33c into 4b3fd52
2 parents 4b3fd52 + 86ce33c commit 84ee747

File tree

4 files changed

+225
-50
lines changed

4 files changed

+225
-50
lines changed

ydb/tests/olap/scenario/helpers/data_generators.py

+26
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,32 @@ def generate_value(self, column: ScenarioTestHelper.Column) -> Any:
6868
return self._value
6969

7070

71+
class ColumnValueGeneratorLambda(IColumnValueGenerator):
72+
"""Arbitrary value generator.
73+
74+
Uses arbitrary function to generate values."""
75+
76+
def __init__(self, func) -> None:
77+
"""Constructor.
78+
79+
Args:
80+
func: Function used to generate values.
81+
Example:
82+
DataGeneratorPerColumn(
83+
self.schema2, 10,
84+
ColumnValueGeneratorDefault(init_value=10))
85+
.with_column('not_level', ColumnValueGeneratorLambda(lambda: time.now())
86+
)
87+
"""
88+
89+
super().__init__()
90+
self._func = func
91+
92+
@override
93+
def generate_value(self, column: ScenarioTestHelper.Column) -> Any:
94+
return self._func()
95+
96+
7197
class ColumnValueGeneratorRandom(IColumnValueGenerator):
7298
"""Random column value generator.
7399

ydb/tests/olap/scenario/helpers/scenario_tests_helper.py

+56-14
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ydb.tests.olap.lib.ydb_cluster import YdbCluster
99
from abc import abstractmethod, ABC
1010
from typing import Set, List, Dict, Any, Callable
11+
from time import sleep
1112

1213

1314
class TestContext:
@@ -59,6 +60,13 @@ class ScenarioTestHelper:
5960
sth.execute_scheme_query(DropTable(table_name))
6061
"""
6162

63+
DEFAULT_RETRIABLE_ERRORS = {
64+
ydb.StatusCode.OVERLOADED,
65+
ydb.StatusCode.BAD_SESSION,
66+
ydb.StatusCode.CONNECTION_LOST,
67+
ydb.StatusCode.UNAVAILABLE,
68+
}
69+
6270
class Column:
6371
"""A class that describes a table column."""
6472

@@ -247,21 +255,37 @@ def _add_not_empty(p: str, dir: str):
247255
return result
248256

249257
@staticmethod
250-
def _run_with_expected_status(operation: callable, expected_status: ydb.StatusCode | Set[ydb.StatusCode]):
258+
def _run_with_expected_status(
259+
operation: callable,
260+
expected_status: ydb.StatusCode | Set[ydb.StatusCode],
261+
retriable_status: ydb.StatusCode | Set[ydb.StatusCode] = {},
262+
n_retries=0,
263+
):
251264
if isinstance(expected_status, ydb.StatusCode):
252265
expected_status = {expected_status}
253-
try:
254-
result = operation()
255-
if ydb.StatusCode.SUCCESS not in expected_status:
256-
pytest.fail(
257-
f'Unexpected status: must be in {repr(expected_status)}, but get {repr(ydb.StatusCode.SUCCESS)}'
258-
)
259-
return result
260-
except ydb.issues.Error as e:
261-
allure.attach(f'{repr(e.status)}: {e}', 'request status', allure.attachment_type.TEXT)
262-
if e.status not in expected_status:
263-
pytest.fail(f'Unexpected status: must be in {repr(expected_status)}, but get {repr(e)}')
264-
return None
266+
if isinstance(retriable_status, ydb.StatusCode):
267+
retriable_status = {retriable_status}
268+
269+
result = None
270+
error = None
271+
status = None
272+
for _ in range(n_retries + 1):
273+
try:
274+
result = operation()
275+
error = None
276+
status = ydb.StatusCode.SUCCESS
277+
except ydb.issues.Error as e:
278+
result = None
279+
error = e
280+
status = error.status
281+
allure.attach(f'{repr(status)}: {error}', 'request status', allure.attachment_type.TEXT)
282+
283+
if status in expected_status:
284+
return result
285+
if status not in retriable_status:
286+
pytest.fail(f'Unexpected status: must be in {repr(expected_status)}, but get {repr(error or status)}')
287+
sleep(3)
288+
pytest.fail(f'Retries exceeded with unexpected status: must be in {repr(expected_status)}, but get {repr(error or status)}')
265289

266290
def _bulk_upsert_impl(
267291
self, tablename: str, data_generator: ScenarioTestHelper.IDataGenerator, expected_status: ydb.StatusCode | Set[ydb.StatusCode]
@@ -302,6 +326,8 @@ def execute_scheme_query(
302326
self,
303327
yqlble: ScenarioTestHelper.IYqlble,
304328
expected_status: ydb.StatusCode | Set[ydb.StatusCode] = ydb.StatusCode.SUCCESS,
329+
retries=0,
330+
retriable_status: ydb.StatusCode | Set[ydb.StatusCode] = DEFAULT_RETRIABLE_ERRORS,
305331
comment: str = '',
306332
) -> None:
307333
"""Run a schema query on the database under test.
@@ -330,7 +356,7 @@ def execute_scheme_query(
330356
yql = yqlble.to_yql(self.test_context)
331357
allure.attach(yql, 'request', allure.attachment_type.TEXT)
332358
self._run_with_expected_status(
333-
lambda: YdbCluster.get_ydb_driver().table_client.session().create().execute_scheme(yql), expected_status
359+
lambda: YdbCluster.get_ydb_driver().table_client.session().create().execute_scheme(yql), expected_status, retriable_status, retries
334360
)
335361

336362
@classmethod
@@ -524,6 +550,22 @@ def get_table_rows_count(self, tablename: str, comment: str = '') -> int:
524550
result_set = self.execute_scan_query(f'SELECT count(*) FROM `{self.get_full_path(tablename)}`')
525551
return result_set.result_set.rows[0][0]
526552

553+
@allure.step('Describe table {path}')
554+
def describe_table(self, path: str, settings: ydb.DescribeTableSettings = None) -> ydb.TableSchemeEntry:
555+
"""Get table description.
556+
557+
Args:
558+
path: Relative path to a table.
559+
settings: DescribeTableSettings.
560+
561+
Returns:
562+
TableSchemeEntry object.
563+
"""
564+
565+
return self._run_with_expected_status(
566+
lambda: YdbCluster.get_ydb_driver().table_client.session().create().describe_table(self.get_full_path(path), settings), ydb.StatusCode.SUCCESS
567+
)
568+
527569
@allure.step('List path {path}')
528570
def list_path(self, path: str) -> List[ydb.SchemeEntry]:
529571
"""Recursively describe the path in the database under test.

0 commit comments

Comments
 (0)