Skip to content

Conversation

sinhasubham
Copy link
Contributor

Add lazy decode to partitioned query

This commit introduces a lazy_decode option to BatchSnapshot.run_partitioned_query. When set to True, the result set yields raw protobuf objects instead of decoded Python objects.

This allows the CPU-intensive decoding work to be deferred and managed by the caller, which can significantly improve performance in multi-threaded applications by reducing GIL contention.

To support this, MergedResultSet now includes decode_row() and decode_column() methods for manual decoding of results.

@product-auto-label product-auto-label bot added size: m Pull request size is medium. api: spanner Issues related to the googleapis/python-spanner API. labels Sep 16, 2025
@sinhasubham sinhasubham marked this pull request as ready for review September 23, 2025 11:42
@sinhasubham sinhasubham requested review from a team as code owners September 23, 2025 11:42
*,
retry=gapic_v1.method.DEFAULT,
timeout=gapic_v1.method.DEFAULT,
lazy_decode=False,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: add documentation for this new argument

@@ -0,0 +1,128 @@
# Copyright 2024 Google LLC All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: 2025

Comment on lines 156 to 191
def decode_row(self, row: []) -> []:
"""Decodes a row from protobuf values to Python objects. This function
should only be called for result sets that use ``lazy_decoding=True``.
The array that is returned by this function is the same as the array
that would have been returned by the rows iterator if ``lazy_decoding=False``.
:returns: an array containing the decoded values of all the columns in the given row
"""
if not isinstance(row, (list, tuple)):
raise TypeError("row must be an array of protobuf values")
decoders = self._decoders
return [
_parse_nullable(row[index], decoders[index]) for index in range(len(row))
]

def decode_column(self, row: [], column_index: int):
"""Decodes a column from a protobuf value to a Python object. This function
should only be called for result sets that use ``lazy_decoding=True``.
The object that is returned by this function is the same as the object
that would have been returned by the rows iterator if ``lazy_decoding=False``.
:returns: the decoded column value
"""
if not isinstance(row, (list, tuple)):
raise TypeError("row must be an array of protobuf values")
decoders = self._decoders
return _parse_nullable(row[column_index], decoders[column_index])

@property
def _decoders(self):
if self.metadata is None:
raise ValueError("iterator not started")
return [
_get_type_decoder(field.type_, field.name, None)
for field in self.metadata.row_type.fields
]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this code really needed? A MergedResultSet just delegates all underlying logic to the 'normal' ResultSets. Those already support lazy decoding. So could we not just call the decode_row/decode_column function of one of the underlying ResultSets instead?

"""
if not isinstance(row, (list, tuple)):
raise TypeError("row must be an array of protobuf values")
decoders = self._decoders
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we cache this instead of reconstructing it for every row?

@sinhasubham sinhasubham enabled auto-merge (squash) October 9, 2025 10:26
@sinhasubham sinhasubham merged commit a09961b into main Oct 9, 2025
21 of 23 checks passed
@sinhasubham sinhasubham deleted the gilContentionLazy branch October 9, 2025 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: spanner Issues related to the googleapis/python-spanner API. size: m Pull request size is medium.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants