Skip to content

Commit d44bf02

Browse files
feat: add total slot ms to RowIterator (#2233)
* feat: add total slot ms to RowIterator * format fix --------- Co-authored-by: Chalmer Lowe <[email protected]>
1 parent 3ed0a0a commit d44bf02

File tree

8 files changed

+47
-0
lines changed

8 files changed

+47
-0
lines changed

google/cloud/bigquery/_job_helpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ def do_query():
560560
num_dml_affected_rows=query_results.num_dml_affected_rows,
561561
query=query,
562562
total_bytes_processed=query_results.total_bytes_processed,
563+
slot_millis=query_results.slot_millis,
563564
)
564565

565566
if job_retry is not None:

google/cloud/bigquery/client.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4144,6 +4144,7 @@ def _list_rows_from_query_results(
41444144
num_dml_affected_rows: Optional[int] = None,
41454145
query: Optional[str] = None,
41464146
total_bytes_processed: Optional[int] = None,
4147+
slot_millis: Optional[int] = None,
41474148
) -> RowIterator:
41484149
"""List the rows of a completed query.
41494150
See
@@ -4195,6 +4196,8 @@ def _list_rows_from_query_results(
41954196
The query text used.
41964197
total_bytes_processed (Optional[int]):
41974198
total bytes processed from job statistics, if present.
4199+
slot_millis (Optional[int]):
4200+
Number of slot ms the user is actually billed for.
41984201
41994202
Returns:
42004203
google.cloud.bigquery.table.RowIterator:
@@ -4234,6 +4237,7 @@ def _list_rows_from_query_results(
42344237
num_dml_affected_rows=num_dml_affected_rows,
42354238
query=query,
42364239
total_bytes_processed=total_bytes_processed,
4240+
slot_millis=slot_millis,
42374241
)
42384242
return row_iterator
42394243

google/cloud/bigquery/job/query.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,7 @@ def is_job_done():
17661766
num_dml_affected_rows=self._query_results.num_dml_affected_rows,
17671767
query=self.query,
17681768
total_bytes_processed=self.total_bytes_processed,
1769+
slot_millis=self.slot_millis,
17691770
**list_rows_kwargs,
17701771
)
17711772
rows._preserve_order = _contains_order_by(self.query)

google/cloud/bigquery/query.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,6 +1282,20 @@ def total_bytes_processed(self):
12821282
if total_bytes_processed is not None:
12831283
return int(total_bytes_processed)
12841284

1285+
@property
1286+
def slot_millis(self):
1287+
"""Total number of slot ms the user is actually billed for.
1288+
1289+
See:
1290+
https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query#body.QueryResponse.FIELDS.slot_millis
1291+
1292+
Returns:
1293+
Optional[int]: Count generated on the server (None until set by the server).
1294+
"""
1295+
slot_millis = self._properties.get("totalSlotMs")
1296+
if slot_millis is not None:
1297+
return int(slot_millis)
1298+
12851299
@property
12861300
def num_dml_affected_rows(self):
12871301
"""Total number of rows affected by a DML query.

google/cloud/bigquery/table.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1812,6 +1812,7 @@ def __init__(
18121812
num_dml_affected_rows: Optional[int] = None,
18131813
query: Optional[str] = None,
18141814
total_bytes_processed: Optional[int] = None,
1815+
slot_millis: Optional[int] = None,
18151816
):
18161817
super(RowIterator, self).__init__(
18171818
client,
@@ -1841,6 +1842,7 @@ def __init__(
18411842
self._num_dml_affected_rows = num_dml_affected_rows
18421843
self._query = query
18431844
self._total_bytes_processed = total_bytes_processed
1845+
self._slot_millis = slot_millis
18441846

18451847
@property
18461848
def _billing_project(self) -> Optional[str]:
@@ -1898,6 +1900,11 @@ def total_bytes_processed(self) -> Optional[int]:
18981900
"""total bytes processed from job statistics, if present."""
18991901
return self._total_bytes_processed
19001902

1903+
@property
1904+
def slot_millis(self) -> Optional[int]:
1905+
"""Number of slot ms the user is actually billed for."""
1906+
return self._slot_millis
1907+
19011908
def _is_almost_completely_cached(self):
19021909
"""Check if all results are completely cached.
19031910

tests/unit/job/test_query.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,7 @@ def test_result_reloads_job_state_until_done(self):
888888
job_resource = self._make_resource(started=True, location="EU")
889889
job_resource_done = self._make_resource(started=True, ended=True, location="EU")
890890
job_resource_done["statistics"]["query"]["totalBytesProcessed"] = str(1234)
891+
job_resource_done["statistics"]["query"]["totalSlotMs"] = str(5678)
891892
job_resource_done["configuration"]["query"]["destinationTable"] = {
892893
"projectId": "dest-project",
893894
"datasetId": "dest_dataset",
@@ -969,6 +970,7 @@ def test_result_reloads_job_state_until_done(self):
969970
self.assertEqual(result.total_rows, 1)
970971
self.assertEqual(result.query, job.query)
971972
self.assertEqual(result.total_bytes_processed, 1234)
973+
self.assertEqual(result.slot_millis, 5678)
972974

973975
query_results_path = f"/projects/{self.PROJECT}/queries/{self.JOB_ID}"
974976
query_results_call = mock.call(

tests/unit/test_client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5718,6 +5718,7 @@ def test_query_and_wait_defaults(self):
57185718
"rows": [{"f": [{"v": "5552452"}]}],
57195719
"queryId": "job_abcDEF_",
57205720
"totalBytesProcessed": 1234,
5721+
"totalSlotMs": 5678,
57215722
}
57225723
creds = _make_credentials()
57235724
http = object()
@@ -5735,6 +5736,7 @@ def test_query_and_wait_defaults(self):
57355736
self.assertIsNone(rows.location)
57365737
self.assertEqual(rows.query, query)
57375738
self.assertEqual(rows.total_bytes_processed, 1234)
5739+
self.assertEqual(rows.slot_millis, 5678)
57385740

57395741
# Verify the request we send is to jobs.query.
57405742
conn.api_request.assert_called_once()

tests/unit/test_query.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2000,6 +2000,22 @@ def test_total_bytes_processed_present_string(self):
20002000
query = self._make_one(resource)
20012001
self.assertEqual(query.total_bytes_processed, 123456)
20022002

2003+
def test_slot_millis_missing(self):
2004+
query = self._make_one(self._make_resource())
2005+
self.assertIsNone(query.slot_millis)
2006+
2007+
def test_slot_millis_present_integer(self):
2008+
resource = self._make_resource()
2009+
resource["totalSlotMs"] = 123456
2010+
query = self._make_one(resource)
2011+
self.assertEqual(query.slot_millis, 123456)
2012+
2013+
def test_slot_millis_present_string(self):
2014+
resource = self._make_resource()
2015+
resource["totalSlotMs"] = "123456"
2016+
query = self._make_one(resource)
2017+
self.assertEqual(query.slot_millis, 123456)
2018+
20032019
def test_num_dml_affected_rows_missing(self):
20042020
query = self._make_one(self._make_resource())
20052021
self.assertIsNone(query.num_dml_affected_rows)

0 commit comments

Comments
 (0)