Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1.8k
Retry streaming exceptions (redux)#3930
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
487d1782ce70408817a3283343542abaccf7b7221efc259641f688a6db9a2ab6ce9864c2356b8a9928567d838168f47fe2File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -34,12 +34,20 @@ class StreamedResultSet(object): | ||
| :class:`google.cloud.proto.spanner.v1.result_set_pb2.PartialResultSet` | ||
| instances. | ||
| :type restart: callable | ||
| :param restart: | ||
| Function (typically curried via :func:`functools.partial`) used to | ||
| restart the initial request if a retriable error is raised during | ||
| streaming. | ||
| :type source: :class:`~google.cloud.spanner.snapshot.Snapshot` | ||
| :param source: Snapshot from which the result set was fetched. | ||
| """ | ||
| def __init__(self, response_iterator, source=None): | ||
| def __init__(self, response_iterator, restart, source=None): | ||
| self._response_iterator = response_iterator | ||
| self._rows = [] # Fully-processed rows | ||
| self._restart = restart | ||
| self._pending_rows = [] # Rows pending new token / EOT | ||
| self._complete_rows = [] # Fully-processed rows | ||
| self._counter = 0 # Counter for processed responses | ||
| self._metadata = None # Until set from first PRS | ||
| self._stats = None # Until set from last PRS | ||
| @@ -55,7 +63,7 @@ def rows(self): | ||
| :rtype: list of row-data lists. | ||
| :returns: list of completed row data, from proceesd PRS responses. | ||
| """ | ||
| return self._rows | ||
| return self._complete_rows | ||
| @property | ||
| def fields(self): | ||
| @@ -122,17 +130,50 @@ def _merge_values(self, values): | ||
| field = self.fields[index] | ||
| self._current_row.append(_parse_value_pb(value, field.type)) | ||
| if len(self._current_row) == width: | ||
| self._rows.append(self._current_row) | ||
| self._pending_rows.append(self._current_row) | ||
| self._current_row = [] | ||
| def _flush_pending_rows(self): | ||
| """Helper for :meth:`consume_next`.""" | ||
| flushed = self._pending_rows[:] | ||
| self._pending_rows[:] = () | ||
| self._complete_rows.extend(flushed) | ||
| def _do_restart(self): | ||
| """Helper for :meth:`consume_next`.""" | ||
| self._pending_chunk = None | ||
| self._pending_rows[:] = () | ||
| self._current_row[:] = () | ||
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page. | ||
| if self._resume_token: | ||
| self._response_iterator = self._restart(self._resume_token) | ||
| else: | ||
| self._response_iterator = self._restart() | ||
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page.
This comment was marked as spam.Sorry, something went wrong. Uh oh!There was an error while loading. Please reload this page. | ||
| def consume_next(self): | ||
| """Consume the next partial result set from the stream. | ||
| Parse the result set into new/existing rows in :attr:`_rows` | ||
| Parse the result set into new/existing rows in :attr:`_complete_rows` | ||
| :raises StopIteration: if the iterator is empty. | ||
| """ | ||
| response = six.next(self._response_iterator) | ||
| try: | ||
| response = six.next(self._response_iterator) | ||
| except exceptions.ServiceUnavailable: | ||
| self._do_restart() | ||
| return | ||
| except StopIteration: | ||
| self._flush_pending_rows() | ||
| raise | ||
| self._counter += 1 | ||
| self._resume_token = response.resume_token | ||
| if response.resume_token: | ||
| self._flush_pending_rows() | ||
| self._resume_token = response.resume_token | ||
| if response.HasField('stats'): # last response | ||
| self._flush_pending_rows() | ||
| self._stats = response.stats | ||
| if self._metadata is None: # first response | ||
| metadata = self._metadata = response.metadata | ||
| @@ -141,9 +182,6 @@ def consume_next(self): | ||
| if source is not None and source._transaction_id is None: | ||
| source._transaction_id = metadata.transaction.id | ||
| if response.HasField('stats'): # last response | ||
| self._stats = response.stats | ||
| values = list(response.values) | ||
| if self._pending_chunk is not None: | ||
| values[0] = self._merge_chunk(values[0]) | ||
| @@ -162,11 +200,15 @@ def consume_all(self): | ||
| break | ||
| def __iter__(self): | ||
| iter_rows, self._rows[:] = self._rows[:], () | ||
| iter_rows, self._complete_rows[:] = self._complete_rows[:], () | ||
| while True: | ||
| if not iter_rows: | ||
| self.consume_next() # raises StopIteration | ||
| iter_rows, self._rows[:] = self._rows[:], () | ||
| try: | ||
| self.consume_next() # raises StopIteration | ||
| except StopIteration: | ||
| if not self._complete_rows: | ||
| raise | ||
| iter_rows, self._complete_rows[:] = self._complete_rows[:], () | ||
| while iter_rows: | ||
| yield iter_rows.pop(0) | ||
| @@ -179,6 +221,7 @@ def one(self): | ||
| in whole or in part. | ||
| """ | ||
| answer = self.one_or_none() | ||
| if answer is None: | ||
| raise exceptions.NotFound('No rows matched the given query.') | ||
| return answer | ||
| @@ -196,21 +239,13 @@ def one_or_none(self): | ||
| raise RuntimeError('Can not call `.one` or `.one_or_none` after ' | ||
| 'stream consumption has already started.') | ||
| # Consume the first result of the stream. | ||
| # If there is no first result, then return None. | ||
| iterator = iter(self) | ||
| try: | ||
| answer = next(iterator) | ||
| except StopIteration: | ||
| return None | ||
| self.consume_all() | ||
| # Attempt to consume more. This should no-op; if we get additional | ||
| # rows, then this is an error case. | ||
| try: | ||
| next(iterator) | ||
| if len(self._complete_rows) > 1: | ||
| raise ValueError('Expected one result; got more.') | ||
| except StopIteration: | ||
| return answer | ||
| if self._complete_rows: | ||
| return self._complete_rows[0] | ||
| class Unmergeable(ValueError): | ||
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
This comment was marked as spam.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.