Skip to content

Commit 135ca56

Browse files
authored
Multiprocess scenario test (#13371)
1 parent 112b4a7 commit 135ca56

File tree

5 files changed

+83
-47
lines changed

5 files changed

+83
-47
lines changed

ydb/tests/olap/scenario/conftest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ def teardown_class(cls):
7777
cls._ydb_instance.stop()
7878

7979
def test(self, ctx: TestContext):
80+
test_path = ctx.test + get_external_param("table_suffix", "")
81+
ScenarioTestHelper(None).remove_path(test_path, ctx.suite)
8082
start_time = time.time()
8183
try:
8284
ctx.executable(self, ctx)
@@ -103,6 +105,7 @@ def test(self, ctx: TestContext):
103105
allure_test_description(ctx.suite, ctx.test, start_time=start_time, end_time=time.time())
104106
raise
105107
allure_test_description(ctx.suite, ctx.test, start_time=start_time, end_time=time.time())
108+
ScenarioTestHelper(None).remove_path(test_path, ctx.suite)
106109

107110

108111
def pytest_generate_tests(metafunc):

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

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from abc import abstractmethod, ABC
1111
from typing import Set, List, Dict, Any, Callable, Optional
1212
from time import sleep
13+
from ydb.tests.olap.lib.utils import get_external_param
1314

1415

1516
class TestContext:
@@ -316,7 +317,7 @@ def _add_not_empty(p: str, dir: str):
316317
result = os.path.join('/', YdbCluster.ydb_database, YdbCluster.tables_path)
317318
if self.test_context is not None:
318319
result = _add_not_empty(result, self.test_context.suite)
319-
result = _add_not_empty(result, self.test_context.test)
320+
result = _add_not_empty(result, self.test_context.test) + get_external_param("table_suffix", "")
320321
result = _add_not_empty(result, path)
321322
return result
322323

@@ -463,7 +464,7 @@ def execute_scan_query(
463464

464465
@allure.step('Execute query')
465466
def execute_query(
466-
self, yql: str, expected_status: ydb.StatusCode | Set[ydb.StatusCode] = ydb.StatusCode.SUCCESS
467+
self, yql: str, expected_status: ydb.StatusCode | Set[ydb.StatusCode] = ydb.StatusCode.SUCCESS, retries=0
467468
):
468469
"""Run a query on the tested database.
469470
@@ -479,7 +480,7 @@ def execute_query(
479480

480481
allure.attach(yql, 'request', allure.attachment_type.TEXT)
481482
with ydb.QuerySessionPool(YdbCluster.get_ydb_driver()) as pool:
482-
self._run_with_expected_status(lambda: pool.execute_with_retries(yql), expected_status)
483+
self._run_with_expected_status(lambda: pool.execute_with_retries(yql, None, ydb.RetrySettings(max_retries=retries)), expected_status)
483484

484485
def drop_if_exist(self, names: List[str], operation) -> None:
485486
"""Erase entities in the tested database, if it exists.
@@ -653,7 +654,7 @@ def describe_table(self, path: str, settings: ydb.DescribeTableSettings = None)
653654
)
654655

655656
@allure.step('List path {path}')
656-
def list_path(self, path: str) -> List[ydb.SchemeEntry]:
657+
def list_path(self, path: str, folder: str) -> List[ydb.SchemeEntry]:
657658
"""Recursively describe the path in the database under test.
658659
659660
If the path is a directory or TableStore, then all subpaths are included in the description.
@@ -666,7 +667,7 @@ def list_path(self, path: str) -> List[ydb.SchemeEntry]:
666667
If the path does not exist, an empty list is returned.
667668
"""
668669

669-
root_path = self.get_full_path('')
670+
root_path = self.get_full_path(folder)
670671
try:
671672
self_descr = YdbCluster._describe_path_impl(os.path.join(root_path, path))
672673
except ydb.issues.SchemeError:
@@ -681,7 +682,7 @@ def list_path(self, path: str) -> List[ydb.SchemeEntry]:
681682
return self_descr
682683

683684
@allure.step('Remove path {path}')
684-
def remove_path(self, path: str) -> None:
685+
def remove_path(self, path: str, folder: str = '') -> None:
685686
"""Recursively delete a path in the tested database.
686687
687688
If the path is a directory or TableStore, then all nested paths are removed.
@@ -696,12 +697,12 @@ def remove_path(self, path: str) -> None:
696697

697698
import ydb.tests.olap.scenario.helpers.drop_helper as dh
698699

699-
root_path = self.get_full_path('')
700-
for e in self.list_path(path):
700+
root_path = self.get_full_path(folder)
701+
for e in self.list_path(path, folder):
701702
if e.is_any_table():
702-
self.execute_scheme_query(dh.DropTable(e.name))
703+
self.execute_scheme_query(dh.DropTable(os.path.join(folder, e.name)))
703704
elif e.is_column_store():
704-
self.execute_scheme_query(dh.DropTableStore(e.name))
705+
self.execute_scheme_query(dh.DropTableStore(os.path.join(folder, e.name)))
705706
elif e.is_directory():
706707
self._run_with_expected_status(
707708
lambda: YdbCluster.get_ydb_driver().scheme_client.remove_directory(os.path.join(root_path, e.name)),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh -e
2+
make S3_ACCESS_KEY=$1 S3_SECRET_KEY=$2 YDB_ENDPOINT=$3 YDB_DB=$4 -rkj -f test.mk all.test.dst && echo OK || echo Error

ydb/tests/olap/scenario/test.mk

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
suffixes:=$(shell jot 20)
2+
3+
$(suffixes:=.test.dst): %.test.dst:
4+
../../../../ya test --build=relwithdebinfo --test-disable-timeout --test-param ydb-endpoint=$(YDB_ENDPOINT) --test-param ydb-db=$(YDB_DB) --test-param tables-path=scenario --test-param s3-endpoint=http://storage.yandexcloud.net --test-param s3-access-key=$(S3_ACCESS_KEY) --test-param s3-secret-key=$(S3_SECRET_KEY) --test-param s3-buckets=ydb-test-test,ydb-test-test-2 --test-param test-duration-seconds=2400 --test-param table_suffix=$* --test-param rows_count=100 --test-param batches_count=1000 --test-param reuse-tables=True --test-param keep-tables=True --test-param tables_count=10 --test-param ignore_read_errors=True -F test_insert.py::TestInsert::test[read_data_during_bulk_upsert]
5+
6+
all.test.dst: $(suffixes:=.test.dst)

ydb/tests/olap/scenario/test_insert.py

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from helpers.thread_helper import TestThread
88
from ydb import PrimitiveType
99
from typing import List, Dict, Any
10-
from ydb.tests.olap.lib.utils import get_external_param
10+
from ydb.tests.olap.lib.utils import get_external_param, external_param_is_true
1111

1212

1313
class TestInsert(BaseTestSet):
@@ -24,19 +24,26 @@ class TestInsert(BaseTestSet):
2424
.with_key_columns("key")
2525
)
2626

27-
def _loop_upsert(self, ctx: TestContext, data: list):
27+
def _loop_upsert(self, ctx: TestContext, data: list, table: str):
2828
sth = ScenarioTestHelper(ctx)
29+
table_name = "log" + table
2930
for batch in data:
30-
sth.bulk_upsert_data("log", self.schema_log, batch)
31+
sth.bulk_upsert_data(table_name, self.schema_log, batch)
3132

32-
def _loop_insert(self, ctx: TestContext, rows_count: int):
33+
def _loop_insert(self, ctx: TestContext, rows_count: int, table: str, ignore_read_errors: bool):
3334
sth = ScenarioTestHelper(ctx)
34-
log: str = sth.get_full_path("log")
35-
cnt: str = sth.get_full_path("cnt")
35+
log: str = sth.get_full_path("log" + table)
36+
cnt: str = sth.get_full_path("cnt" + table)
3637
for i in range(rows_count):
37-
sth.execute_query(
38-
f'$cnt = SELECT CAST(COUNT(*) AS INT64) from `{log}`; INSERT INTO `{cnt}` (key, c) values({i}, $cnt)'
39-
)
38+
try:
39+
sth.execute_query(
40+
yql=f'$cnt = SELECT CAST(COUNT(*) AS INT64) from `{log}`; INSERT INTO `{cnt}` (key, c) values({i}, $cnt)', retries=10
41+
)
42+
except Exception:
43+
if ignore_read_errors:
44+
pass
45+
else:
46+
raise
4047

4148
def scenario_read_data_during_bulk_upsert(self, ctx: TestContext):
4249
sth = ScenarioTestHelper(ctx)
@@ -45,42 +52,59 @@ def scenario_read_data_during_bulk_upsert(self, ctx: TestContext):
4552
batches_count = int(get_external_param("batches_count", "10"))
4653
rows_count = int(get_external_param("rows_count", "1000"))
4754
inserts_count = int(get_external_param("inserts_count", "200"))
48-
sth.execute_scheme_query(
49-
CreateTable(cnt_table_name).with_schema(self.schema_cnt)
50-
)
51-
sth.execute_scheme_query(
52-
CreateTable(log_table_name).with_schema(self.schema_log)
53-
)
55+
tables_count = int(get_external_param("tables_count", "1"))
56+
ignore_read_errors = external_param_is_true("ignore_read_errors")
57+
for table in range(tables_count):
58+
sth.execute_scheme_query(
59+
CreateTable(cnt_table_name + str(table)).with_schema(self.schema_cnt)
60+
)
61+
for table in range(tables_count):
62+
sth.execute_scheme_query(
63+
CreateTable(log_table_name + str(table)).with_schema(self.schema_log)
64+
)
5465
data: List = []
5566
for i in range(batches_count):
5667
batch: List[Dict[str, Any]] = []
5768
for j in range(rows_count):
5869
batch.append({"key": j + rows_count * i})
5970
data.append(batch)
6071

61-
thread1 = TestThread(target=self._loop_upsert, args=[ctx, data])
62-
thread2 = TestThread(target=self._loop_insert, args=[ctx, inserts_count])
72+
thread1 = []
73+
thread2 = []
74+
for table in range(tables_count):
75+
thread1.append(TestThread(target=self._loop_upsert, args=[ctx, data, str(table)]))
76+
for table in range(tables_count):
77+
thread2.append(TestThread(target=self._loop_insert, args=[ctx, inserts_count, str(table), ignore_read_errors]))
78+
79+
for thread in thread1:
80+
thread.start()
6381

64-
thread1.start()
65-
thread2.start()
82+
for thread in thread2:
83+
thread.start()
6684

67-
thread2.join()
68-
thread1.join()
85+
for thread in thread2:
86+
thread.join()
6987

70-
rows: int = sth.get_table_rows_count(cnt_table_name)
71-
assert rows == inserts_count
72-
scan_result = sth.execute_scan_query(
73-
f"SELECT key, c FROM `{sth.get_full_path(cnt_table_name)}` ORDER BY key"
74-
)
75-
for i in range(rows):
76-
if scan_result.result_set.rows[i]["key"] != i:
77-
assert False, f"{i} ?= {scan_result.result_set.rows[i]['key']}"
88+
for thread in thread1:
89+
thread.join()
7890

79-
rows: int = sth.get_table_rows_count(log_table_name)
80-
assert rows == rows_count * batches_count
81-
scan_result = sth.execute_scan_query(
82-
f"SELECT key FROM `{sth.get_full_path(log_table_name)}` ORDER BY key"
83-
)
84-
for i in range(rows):
85-
if scan_result.result_set.rows[i]["key"] != i:
86-
assert False, f"{i} ?= {scan_result.result_set.rows[i]['key']}"
91+
for table in range(tables_count):
92+
cnt_table_name0 = cnt_table_name + str(table)
93+
rows: int = sth.get_table_rows_count(cnt_table_name0)
94+
assert rows == inserts_count
95+
scan_result = sth.execute_scan_query(
96+
f"SELECT key, c FROM `{sth.get_full_path(cnt_table_name0)}` ORDER BY key"
97+
)
98+
for i in range(rows):
99+
if scan_result.result_set.rows[i]["key"] != i:
100+
assert False, f"{i} ?= {scan_result.result_set.rows[i]['key']}"
101+
102+
log_table_name0 = log_table_name + str(table)
103+
rows: int = sth.get_table_rows_count(log_table_name0)
104+
assert rows == rows_count * batches_count
105+
scan_result = sth.execute_scan_query(
106+
f"SELECT key FROM `{sth.get_full_path(log_table_name0)}` ORDER BY key"
107+
)
108+
for i in range(rows):
109+
if scan_result.result_set.rows[i]["key"] != i:
110+
assert False, f"{i} ?= {scan_result.result_set.rows[i]['key']}"

0 commit comments

Comments
 (0)