Skip to content

Commit 16ae5a5

Browse files
committed
QueryService stats support
1 parent 30b4191 commit 16ae5a5

File tree

9 files changed

+147
-24
lines changed

9 files changed

+147
-24
lines changed

tests/query/test_query_session.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from concurrent.futures import _base as b
55
from unittest import mock
66

7-
7+
from ydb.query.base import QueryStatsMode
88
from ydb.query.session import QuerySession
99

1010

@@ -143,3 +143,34 @@ def cancel(self):
143143
assert "attach stream thread" not in thread_names
144144

145145
_check_session_state_empty(session)
146+
147+
@pytest.mark.parametrize(
148+
"stats_mode",
149+
[
150+
None,
151+
QueryStatsMode.UNSPECIFIED,
152+
QueryStatsMode.NONE,
153+
QueryStatsMode.BASIC,
154+
QueryStatsMode.FULL,
155+
QueryStatsMode.PROFILE,
156+
],
157+
)
158+
def test_stats_mode(self, session: QuerySession, stats_mode: QueryStatsMode):
159+
session.create()
160+
161+
for _ in session.execute("SELECT 1; SELECT 2; SELECT 3;", stats_mode=stats_mode):
162+
pass
163+
164+
stats = session.last_query_stats
165+
166+
if stats_mode in [None, QueryStatsMode.NONE, QueryStatsMode.UNSPECIFIED]:
167+
assert stats is None
168+
return
169+
170+
assert stats is not None
171+
assert len(stats.query_phases) > 0
172+
173+
if stats_mode != QueryStatsMode.BASIC:
174+
assert len(stats.query_plan) > 0
175+
else:
176+
assert stats.query_plan == ""

tests/query/test_query_transaction.py

+30
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
22

3+
from ydb.query.base import QueryStatsMode
34
from ydb.query.transaction import QueryTxContext
45
from ydb.query.transaction import QueryTxStateEnum
56

@@ -104,3 +105,32 @@ def test_tx_identity_after_begin_works(self, tx: QueryTxContext):
104105

105106
assert identity.tx_id == tx.tx_id
106107
assert identity.session_id == tx.session_id
108+
109+
@pytest.mark.parametrize(
110+
"stats_mode",
111+
[
112+
None,
113+
QueryStatsMode.UNSPECIFIED,
114+
QueryStatsMode.NONE,
115+
QueryStatsMode.BASIC,
116+
QueryStatsMode.FULL,
117+
QueryStatsMode.PROFILE,
118+
],
119+
)
120+
def test_stats_mode(self, tx: QueryTxContext, stats_mode: QueryStatsMode):
121+
for _ in tx.execute("SELECT 1; SELECT 2; SELECT 3;", commit_tx=True, stats_mode=stats_mode):
122+
pass
123+
124+
stats = tx.last_query_stats
125+
126+
if stats_mode in [None, QueryStatsMode.NONE, QueryStatsMode.UNSPECIFIED]:
127+
assert stats is None
128+
return
129+
130+
assert stats is not None
131+
assert len(stats.query_phases) > 0
132+
133+
if stats_mode != QueryStatsMode.BASIC:
134+
assert len(stats.query_plan) > 0
135+
else:
136+
assert stats.query_plan == ""

ydb/aio/query/pool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ async def retry_tx_async(
142142
"""Special interface to execute a bunch of commands with transaction in a safe, retriable way.
143143
144144
:param callee: A function, that works with session.
145-
:param tx_mode: Transaction mode, which is a one from the following choises:
145+
:param tx_mode: Transaction mode, which is a one from the following choices:
146146
1) QuerySerializableReadWrite() which is default mode;
147147
2) QueryOnlineReadOnly(allow_inconsistent_reads=False);
148148
3) QuerySnapshotReadOnly();

ydb/aio/query/session.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -117,26 +117,34 @@ async def execute(
117117
exec_mode: base.QueryExecMode = None,
118118
concurrent_result_sets: bool = False,
119119
settings: Optional[BaseRequestSettings] = None,
120+
*,
121+
stats_mode: Optional[base.QueryStatsMode] = None,
120122
) -> AsyncResponseContextIterator:
121123
"""Sends a query to Query Service
122124
123125
:param query: (YQL or SQL text) to be executed.
124-
:param syntax: Syntax of the query, which is a one from the following choises:
126+
:param syntax: Syntax of the query, which is a one from the following choices:
125127
1) QuerySyntax.YQL_V1, which is default;
126128
2) QuerySyntax.PG.
127129
:param parameters: dict with parameters and YDB types;
128130
:param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False;
131+
:param stats_mode: Mode of query statistics to gather, which is a one from the following choices:
132+
1) QueryStatsMode:NONE, which is default;
133+
2) QueryStatsMode.BASIC;
134+
3) QueryStatsMode.FULL;
135+
4) QueryStatsMode.PROFILE;
129136
130137
:return: Iterator with result sets
131138
"""
132139
self._state._check_session_ready_to_use()
133140

134141
stream_it = await self._execute_call(
135142
query=query,
143+
parameters=parameters,
136144
commit_tx=True,
137145
syntax=syntax,
138146
exec_mode=exec_mode,
139-
parameters=parameters,
147+
stats_mode=stats_mode,
140148
concurrent_result_sets=concurrent_result_sets,
141149
settings=settings,
142150
)
@@ -147,6 +155,7 @@ async def execute(
147155
rpc_state=None,
148156
response_pb=resp,
149157
session_state=self._state,
158+
session=self,
150159
settings=self._settings,
151160
),
152161
)

ydb/aio/query/transaction.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def __init__(self, driver, session_state, session, tx_mode):
2929
3030
:param driver: A driver instance
3131
:param session_state: A state of session
32-
:param tx_mode: Transaction mode, which is a one from the following choises:
32+
:param tx_mode: Transaction mode, which is a one from the following choices:
3333
1) QuerySerializableReadWrite() which is default mode;
3434
2) QueryOnlineReadOnly(allow_inconsistent_reads=False);
3535
3) QuerySnapshotReadOnly();
@@ -142,32 +142,40 @@ async def execute(
142142
exec_mode: Optional[base.QueryExecMode] = None,
143143
concurrent_result_sets: Optional[bool] = False,
144144
settings: Optional[BaseRequestSettings] = None,
145+
*,
146+
stats_mode: Optional[base.QueryStatsMode] = None,
145147
) -> AsyncResponseContextIterator:
146148
"""Sends a query to Query Service
147149
148150
:param query: (YQL or SQL text) to be executed.
149151
:param parameters: dict with parameters and YDB types;
150152
:param commit_tx: A special flag that allows transaction commit.
151-
:param syntax: Syntax of the query, which is a one from the following choises:
153+
:param syntax: Syntax of the query, which is a one from the following choices:
152154
1) QuerySyntax.YQL_V1, which is default;
153155
2) QuerySyntax.PG.
154-
:param exec_mode: Exec mode of the query, which is a one from the following choises:
156+
:param exec_mode: Exec mode of the query, which is a one from the following choices:
155157
1) QueryExecMode.EXECUTE, which is default;
156158
2) QueryExecMode.EXPLAIN;
157159
3) QueryExecMode.VALIDATE;
158160
4) QueryExecMode.PARSE.
159161
:param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False;
162+
:param stats_mode: Mode of query statistics to gather, which is a one from the following choices:
163+
1) QueryStatsMode:NONE, which is default;
164+
2) QueryStatsMode.BASIC;
165+
3) QueryStatsMode.FULL;
166+
4) QueryStatsMode.PROFILE;
160167
161168
:return: Iterator with result sets
162169
"""
163170
await self._ensure_prev_stream_finished()
164171

165172
stream_it = await self._execute_call(
166173
query=query,
174+
parameters=parameters,
167175
commit_tx=commit_tx,
168176
syntax=syntax,
169177
exec_mode=exec_mode,
170-
parameters=parameters,
178+
stats_mode=stats_mode,
171179
concurrent_result_sets=concurrent_result_sets,
172180
settings=settings,
173181
)

ydb/query/base.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
if typing.TYPE_CHECKING:
2727
from .transaction import BaseQueryTxContext
28+
from .session import BaseQuerySession
2829

2930

3031
class QuerySyntax(enum.IntEnum):
@@ -41,7 +42,7 @@ class QueryExecMode(enum.IntEnum):
4142
EXECUTE = 50
4243

4344

44-
class StatsMode(enum.IntEnum):
45+
class QueryStatsMode(enum.IntEnum):
4546
UNSPECIFIED = 0
4647
NONE = 10
4748
BASIC = 20
@@ -132,12 +133,13 @@ def create_execute_query_request(
132133
tx_mode: Optional[BaseQueryTxMode],
133134
syntax: Optional[QuerySyntax],
134135
exec_mode: Optional[QueryExecMode],
136+
stats_mode: Optional[QueryStatsMode],
135137
parameters: Optional[dict],
136138
concurrent_result_sets: Optional[bool],
137139
) -> ydb_query.ExecuteQueryRequest:
138140
syntax = QuerySyntax.YQL_V1 if not syntax else syntax
139141
exec_mode = QueryExecMode.EXECUTE if not exec_mode else exec_mode
140-
stats_mode = StatsMode.NONE # TODO: choise is not supported yet
142+
stats_mode = QueryStatsMode.NONE if stats_mode is None else stats_mode
141143

142144
tx_control = None
143145
if not tx_id and not tx_mode:
@@ -189,6 +191,7 @@ def wrap_execute_query_response(
189191
response_pb: _apis.ydb_query.ExecuteQueryResponsePart,
190192
session_state: IQuerySessionState,
191193
tx: Optional["BaseQueryTxContext"] = None,
194+
session: Optional["BaseQuerySession"] = None,
192195
commit_tx: Optional[bool] = False,
193196
settings: Optional[QueryClientSettings] = None,
194197
) -> convert.ResultSet:
@@ -198,6 +201,12 @@ def wrap_execute_query_response(
198201
elif tx and response_pb.tx_meta and not tx.tx_id:
199202
tx._move_to_beginned(response_pb.tx_meta.id)
200203

204+
if response_pb.HasField("exec_stats"):
205+
if tx is not None:
206+
tx._last_query_stats = response_pb.exec_stats
207+
if session is not None:
208+
session._last_query_stats = response_pb.exec_stats
209+
201210
if response_pb.HasField("result_set"):
202211
return convert.ResultSet.from_message(response_pb.result_set, settings)
203212

ydb/query/pool.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ def retry_tx_sync(
151151
"""Special interface to execute a bunch of commands with transaction in a safe, retriable way.
152152
153153
:param callee: A function, that works with session.
154-
:param tx_mode: Transaction mode, which is a one from the following choises:
154+
:param tx_mode: Transaction mode, which is a one from the following choices:
155155
1) QuerySerializableReadWrite() which is default mode;
156156
2) QueryOnlineReadOnly(allow_inconsistent_reads=False);
157157
3) QuerySnapshotReadOnly();

ydb/query/session.py

+25-6
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,12 @@ def __init__(self, driver: common_utils.SupportedDriverType, settings: Optional[
147147
.with_timeout(DEFAULT_ATTACH_LONG_TIMEOUT)
148148
)
149149

150+
self._last_query_stats = None
151+
152+
@property
153+
def last_query_stats(self):
154+
return self._last_query_stats
155+
150156
def _get_client_settings(
151157
self,
152158
driver: common_utils.SupportedDriverType,
@@ -189,22 +195,26 @@ def _attach_call(self) -> Iterable[_apis.ydb_query.SessionState]:
189195
def _execute_call(
190196
self,
191197
query: str,
198+
parameters: dict = None,
192199
commit_tx: bool = False,
193200
syntax: base.QuerySyntax = None,
194201
exec_mode: base.QueryExecMode = None,
195-
parameters: dict = None,
202+
stats_mode: Optional[base.QueryStatsMode] = None,
196203
concurrent_result_sets: bool = False,
197204
settings: Optional[BaseRequestSettings] = None,
198205
) -> Iterable[_apis.ydb_query.ExecuteQueryResponsePart]:
206+
self._last_query_stats = None
207+
199208
request = base.create_execute_query_request(
200209
query=query,
201-
session_id=self._state.session_id,
210+
parameters=parameters,
202211
commit_tx=commit_tx,
212+
session_id=self._state.session_id,
203213
tx_mode=None,
204214
tx_id=None,
205215
syntax=syntax,
206216
exec_mode=exec_mode,
207-
parameters=parameters,
217+
stats_mode=stats_mode,
208218
concurrent_result_sets=concurrent_result_sets,
209219
)
210220

@@ -293,7 +303,7 @@ def create(self, settings: Optional[BaseRequestSettings] = None) -> "QuerySessio
293303
def transaction(self, tx_mode: Optional[base.BaseQueryTxMode] = None) -> QueryTxContext:
294304
"""Creates a transaction context manager with specified transaction mode.
295305
296-
:param tx_mode: Transaction mode, which is a one from the following choises:
306+
:param tx_mode: Transaction mode, which is a one from the following choices:
297307
1) QuerySerializableReadWrite() which is default mode;
298308
2) QueryOnlineReadOnly(allow_inconsistent_reads=False);
299309
3) QuerySnapshotReadOnly();
@@ -321,26 +331,34 @@ def execute(
321331
exec_mode: base.QueryExecMode = None,
322332
concurrent_result_sets: bool = False,
323333
settings: Optional[BaseRequestSettings] = None,
334+
*,
335+
stats_mode: Optional[base.QueryStatsMode] = None,
324336
) -> base.SyncResponseContextIterator:
325337
"""Sends a query to Query Service
326338
327339
:param query: (YQL or SQL text) to be executed.
328-
:param syntax: Syntax of the query, which is a one from the following choises:
340+
:param syntax: Syntax of the query, which is a one from the following choices:
329341
1) QuerySyntax.YQL_V1, which is default;
330342
2) QuerySyntax.PG.
331343
:param parameters: dict with parameters and YDB types;
332344
:param concurrent_result_sets: A flag to allow YDB mix parts of different result sets. Default is False;
345+
:param stats_mode: Mode of query statistics to gather, which is a one from the following choices:
346+
1) QueryStatsMode:NONE, which is default;
347+
2) QueryStatsMode.BASIC;
348+
3) QueryStatsMode.FULL;
349+
4) QueryStatsMode.PROFILE;
333350
334351
:return: Iterator with result sets
335352
"""
336353
self._state._check_session_ready_to_use()
337354

338355
stream_it = self._execute_call(
339356
query=query,
357+
parameters=parameters,
340358
commit_tx=True,
341359
syntax=syntax,
342360
exec_mode=exec_mode,
343-
parameters=parameters,
361+
stats_mode=stats_mode,
344362
concurrent_result_sets=concurrent_result_sets,
345363
settings=settings,
346364
)
@@ -351,6 +369,7 @@ def execute(
351369
rpc_state=None,
352370
response_pb=resp,
353371
session_state=self._state,
372+
session=self,
354373
settings=self._settings,
355374
),
356375
)

0 commit comments

Comments
 (0)