Skip to content

Commit 54bc46b

Browse files
authored
Multiplans (#9053)
1 parent a8fb87e commit 54bc46b

File tree

3 files changed

+86
-54
lines changed

3 files changed

+86
-54
lines changed

ydb/public/lib/ydb_cli/commands/ydb_benchmark.cpp

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,6 @@ bool TWorkloadCommandBenchmark::RunBench(TClient& client, NYdbWorkload::IWorkloa
331331
ui32 failsCount = 0;
332332
ui32 diffsCount = 0;
333333
std::optional<TString> prevResult;
334-
bool planSaved = false;
335334
for (ui32 i = 0; i < IterationsCount; ++i) {
336335
auto t1 = TInstant::Now();
337336
TQueryBenchmarkResult res = TQueryBenchmarkResult::Error("undefined", "undefined", "undefined");
@@ -365,35 +364,38 @@ bool TWorkloadCommandBenchmark::RunBench(TClient& client, NYdbWorkload::IWorkloa
365364
} else {
366365
++failsCount;
367366
Cout << "failed\t" << duration << " seconds" << Endl;
368-
Cerr << queryN << ": " << Endl
367+
Cerr << queryN << ":" << Endl
368+
<< "iteration " << i << Endl
369369
<< res.GetErrorInfo() << Endl;
370370
Cerr << "Query text:" << Endl;
371371
Cerr << query << Endl << Endl;
372372
Sleep(TDuration::Seconds(1));
373373
}
374-
if (!planSaved && PlanFileName) {
374+
if (PlanFileName) {
375375
TFsPath(PlanFileName).Parent().MkDirs();
376-
{
377-
TFileOutput out(PlanFileName + ".table");
378-
TQueryPlanPrinter queryPlanPrinter(EDataFormat::PrettyTable, true, out, 120);
379-
queryPlanPrinter.Print(res.GetQueryPlan());
376+
const TString planFName = TStringBuilder() << PlanFileName << "." << i << ".";
377+
if (res.GetQueryPlan()) {
378+
{
379+
TFileOutput out(planFName + "table");
380+
TQueryPlanPrinter queryPlanPrinter(EDataFormat::PrettyTable, true, out, 120);
381+
queryPlanPrinter.Print(res.GetQueryPlan());
382+
}
383+
{
384+
TFileOutput out(planFName + "json");
385+
TQueryPlanPrinter queryPlanPrinter(EDataFormat::JsonBase64, true, out, 120);
386+
queryPlanPrinter.Print(res.GetQueryPlan());
387+
}
388+
{
389+
TPlanVisualizer pv;
390+
pv.LoadPlans(res.GetQueryPlan());
391+
TFileOutput out(planFName + "svg");
392+
out << pv.PrintSvgSafe();
393+
}
380394
}
381-
{
382-
TFileOutput out(PlanFileName + ".json");
383-
TQueryPlanPrinter queryPlanPrinter(EDataFormat::JsonBase64, true, out, 120);
384-
queryPlanPrinter.Print(res.GetQueryPlan());
385-
}
386-
{
387-
TFileOutput out(PlanFileName + ".ast");
395+
if (res.GetPlanAst()) {
396+
TFileOutput out(planFName + "ast");
388397
out << res.GetPlanAst();
389398
}
390-
{
391-
TPlanVisualizer pv;
392-
pv.LoadPlans(res.GetQueryPlan());
393-
TFileOutput out(PlanFileName + ".svg");
394-
out << pv.PrintSvgSafe();
395-
}
396-
planSaved = true;
397399
}
398400
}
399401

ydb/tests/olap/lib/ydb_cli.py

Lines changed: 46 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -35,31 +35,48 @@ def __init__(self, plan: dict | None = None, table: str | None = None, ast: str
3535
class WorkloadRunResult:
3636
def __init__(
3737
self, stats: dict[str, dict[str, any]] = {}, query_out: str = None, stdout: str = None, stderr: str = None,
38-
error_message: str | None = None, plan: YdbCliHelper.QueuePlan | None = None
38+
error_message: str | None = None, plans: list[YdbCliHelper.QueuePlan] | None = None,
39+
errors_by_iter: dict[int, str] | None = None
3940
) -> None:
4041
self.stats = stats
4142
self.query_out = query_out if str != '' else None
4243
self.stdout = stdout if stdout != '' else None
4344
self.stderr = stderr if stderr != '' else None
4445
self.success = error_message is None
4546
self.error_message = '' if self.success else error_message
46-
self.plan = plan
47+
self.plans = plans
48+
self.errors_by_iter = errors_by_iter
4749

4850
@staticmethod
4951
def workload_run(type: WorkloadType, path: str, query_num: int, iterations: int = 5,
5052
timeout: float = 100.) -> YdbCliHelper.WorkloadRunResult:
5153
def _try_extract_error_message(stderr: str) -> str:
54+
result = {}
5255
begin_str = f'{query_num}:'
5356
end_str = 'Query text:'
57+
iter_str = 'iteration '
5458
begin_pos = stderr.find(begin_str)
5559
if begin_pos < 0:
56-
return ''
57-
begin_pos += len(begin_str)
58-
end_pos = stderr.find(end_str, begin_pos)
59-
if end_pos < 0:
60-
return stderr[begin_pos:].strip()
61-
return stderr[begin_pos:end_pos].strip()
60+
return result
61+
while True:
62+
begin_pos = stderr.find(iter_str, begin_pos)
63+
if begin_pos < 0:
64+
return result
65+
begin_pos += len(iter_str)
66+
end_pos = stderr.find('\n', begin_pos)
67+
if end_pos < 0:
68+
iter = int(stderr[begin_pos:])
69+
begin_pos = len(stderr) - 1
70+
else:
71+
iter = int(stderr[begin_pos:end_pos])
72+
begin_pos = end_pos + 1
73+
end_pos = stderr.find(end_str, begin_pos)
74+
if end_pos < 0:
75+
result[iter] = stderr[begin_pos:].strip()
76+
else:
77+
result[iter] = stderr[begin_pos:end_pos].strip()
6278

79+
errors_by_iter = {}
6380
try:
6481
wait_error = YdbCluster.wait_ydb_alive(300, path)
6582
if wait_error is not None:
@@ -88,7 +105,8 @@ def _try_extract_error_message(stderr: str) -> str:
88105
exec: yatest.common.process._Execution = yatest.common.process.execute(cmd, wait=False, check_exit_code=False)
89106
exec.wait(check_exit_code=False, timeout=timeout)
90107
if exec.returncode != 0:
91-
err = _try_extract_error_message(exec.stderr.decode('utf-8'))
108+
errors_by_iter = _try_extract_error_message(exec.stderr.decode('utf-8'))
109+
err = '\n\n'.join([f'Iteration {i}: {e}' for i, e in errors_by_iter.items()])
92110
if not err:
93111
err = f'Invalid return code: {exec.returncode} instesd 0.'
94112
except (yatest.common.process.TimeoutError, yatest.common.process.ExecutionTimeoutError):
@@ -107,27 +125,31 @@ def _try_extract_error_message(stderr: str) -> str:
107125
if (os.path.exists(qout_path)):
108126
with open(qout_path, 'r') as r:
109127
qout = r.read()
110-
plan = YdbCliHelper.QueuePlan()
111-
if (os.path.exists(plan_path + '.json')):
112-
with open(plan_path + '.json') as f:
113-
plan.plan = json.load(f)
114-
if (os.path.exists(plan_path + '.table')):
115-
with open(plan_path + '.table') as f:
116-
plan.table = f.read()
117-
if (os.path.exists(plan_path + '.ast')):
118-
with open(plan_path + '.ast') as f:
119-
plan.ast = f.read()
120-
if (os.path.exists(plan_path + '.svg')):
121-
with open(plan_path + '.svg') as f:
122-
plan.svg = f.read()
128+
plans = []
129+
for i in range(iterations):
130+
plans.append(YdbCliHelper.QueuePlan())
131+
pp = f'{plan_path}.{i}'
132+
if (os.path.exists(f'{pp}.json')):
133+
with open(f'{pp}.json') as f:
134+
plans[i].plan = json.load(f)
135+
if (os.path.exists(f'{pp}.table')):
136+
with open(f'{pp}.table') as f:
137+
plans[i].table = f.read()
138+
if (os.path.exists(f'{pp}.ast')):
139+
with open(f'{pp}.ast') as f:
140+
plans[i].ast = f.read()
141+
if (os.path.exists(f'{pp}.svg')):
142+
with open(f'{pp}.svg') as f:
143+
plans[i].svg = f.read()
123144

124145
return YdbCliHelper.WorkloadRunResult(
125146
stats=stats,
126147
query_out=qout,
127-
plan=plan,
148+
plans=plans,
128149
stdout=exec.stdout.decode('utf-8'),
129150
stderr=exec.stderr.decode('utf-8'),
130-
error_message=err
151+
error_message=err,
152+
errors_by_iter=errors_by_iter
131153
)
132154
except BaseException as e:
133155
return YdbCliHelper.WorkloadRunResult(error_message=str(e))

ydb/tests/olap/load/conftest.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,23 @@ def _get_duraton(stats, field):
4747
stats = {}
4848
if result.query_out is not None:
4949
allure.attach(result.query_out, 'Query output', attachment_type=allure.attachment_type.TEXT)
50-
if result.plan is not None:
51-
if result.plan.plan is not None:
52-
allure.attach(json.dumps(result.plan.plan), 'Plan json', attachment_type=allure.attachment_type.JSON)
53-
if result.plan.table is not None:
54-
allure.attach(result.plan.table, 'Plan table', attachment_type=allure.attachment_type.TEXT)
55-
if result.plan.ast is not None:
56-
allure.attach(result.plan.ast, 'Plan ast', attachment_type=allure.attachment_type.TEXT)
57-
if result.plan.svg is not None:
58-
allure.attach(result.plan.svg, 'Plan svg', attachment_type=allure.attachment_type.SVG)
50+
if result.plans is not None:
51+
for i in range(self.iterations):
52+
try:
53+
with allure.step(f'Iteration {i}'):
54+
plan = result.plans[i]
55+
if plan.plan is not None:
56+
allure.attach(json.dumps(plan.plan), 'Plan json', attachment_type=allure.attachment_type.JSON)
57+
if plan.table is not None:
58+
allure.attach(plan.table, 'Plan table', attachment_type=allure.attachment_type.TEXT)
59+
if plan.ast is not None:
60+
allure.attach(plan.ast, 'Plan ast', attachment_type=allure.attachment_type.TEXT)
61+
if plan.svg is not None:
62+
allure.attach(plan.svg, 'Plan svg', attachment_type=allure.attachment_type.SVG)
63+
if i in result.errors_by_iter:
64+
pytest.fail(result.errors_by_iter[i])
65+
except BaseException:
66+
pass
5967

6068
if result.stdout is not None:
6169
allure.attach(result.stdout, 'Stdout', attachment_type=allure.attachment_type.TEXT)

0 commit comments

Comments
 (0)