public async Task <bool> MoveNext(CancellationToken cancellationToken) { // Try to walk to the next row for the current response being processed. while (_singleResponseRowEnumerator?.MoveNext() != true) { // If not possible, dispose the old enumerator and create a new one from the // next response, if any. _singleResponseRowEnumerator?.Dispose(); if (await _stream.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) { cancellationToken.ThrowIfCancellationRequested(); _singleResponseRowEnumerator = MergeResponseChunks(_stream.ResponseStream.Current); } else { _singleResponseRowEnumerator = null; break; } } // If the current response's enumerator has produced a row, this enumerator should produce // that same row. if (_singleResponseRowEnumerator != null) { cancellationToken.ThrowIfCancellationRequested(); Current = _singleResponseRowEnumerator.Current; return(true); } if (IsRowInProgress && _stream.GrpcCall.GetStatus().StatusCode != StatusCode.Cancelled) { // TODO: Is there something we could/should do here? Stream prematurely closed // with a row in progress } return(false); IEnumerator <Row> MergeResponseChunks(ReadRowsResponse response) { foreach (var chunk in response.Chunks) { if (chunk.ResetRow) { Reset(); continue; } _rowMergeState = _rowMergeState.HandleChunk(this, chunk); if (chunk.CommitRow) { _rowMergeState = _rowMergeState.CommitRow(this); // TODO: Capture response.LastScannedRowKey here for smart retries yield return(_currentCell.Row); } } } }
public async Task <bool> MoveNext(CancellationToken cancellationToken) { if (_stream == null) { await EstablishStream(_requestManager.OriginalRequest).ConfigureAwait(false); } // Try to walk to the next row for the current response being processed. while (_singleResponseRowEnumerator?.MoveNext() != true) { try { // If not possible, dispose the old enumerator and create a new one from the // next response, if any. _singleResponseRowEnumerator?.Dispose(); _singleResponseRowEnumerator = null; if (await _stream.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) { cancellationToken.ThrowIfCancellationRequested(); _singleResponseRowEnumerator = MergeResponseChunks(_stream.ResponseStream.Current); } else { break; } } catch (RpcException e) when(_retrySettings.RetryFilter(e)) { var retryRequest = _requestManager.BuildUpdatedRequest(); if (retryRequest == null) { break; } // Reset the merging and re-connect to a new stream. Reset(); await EstablishStream(retryRequest).ConfigureAwait(false); } } // If the current response's enumerator has produced a row, this enumerator should produce // that same row. if (_singleResponseRowEnumerator != null) { cancellationToken.ThrowIfCancellationRequested(); Current = _singleResponseRowEnumerator.Current; return(true); } if (IsRowInProgress && _stream.GrpcCall?.GetStatus().StatusCode != StatusCode.Cancelled) { throw new InvalidOperationException("The ReadRows stream has ended with a row in progress."); } return(false); Task EstablishStream(ReadRowsRequest request) => ApiCallRetryExtensions.RetryOperationUntilCompleted( () => { _stream = _client.ReadRowsInternal(request, _callSettings); return(Task.FromResult(true)); }, _client.Clock, _client.Scheduler, _callSettings, _retrySettings); IEnumerator <Row> MergeResponseChunks(ReadRowsResponse response) { ByteString previouslyProcessedKey = _lastCompletedRowKey; foreach (var chunk in response.Chunks) { _rowMergeState = _rowMergeState.HandleChunk(this, chunk); if (chunk.CommitRow) { _rowMergeState = _rowMergeState.CommitRow(this); _lastCompletedRowKey = _currentCell.Row.Key; _requestManager.IncrementRowsReadSoFar(); yield return(_currentCell.Row); } } if (previouslyProcessedKey != _lastCompletedRowKey) { // There was a full row found in the response. TryUpdateLastFoundKey(_lastCompletedRowKey); } else { // Otherwise, the service may have indicated that it processed rows that did not match the filter, // and will not need to be reprocessed. TryUpdateLastFoundKey(response.LastScannedRowKey); } void TryUpdateLastFoundKey(ByteString lastProcessedKey) { if (lastProcessedKey != null && !lastProcessedKey.IsEmpty) { _requestManager.LastFoundKey = lastProcessedKey; } } } }
public async Task <bool> MoveNext(CancellationToken cancellationToken) { // Try to walk to the next row for the current response being processed. while (_singleResponseRowEnumerator?.MoveNext() != true) { // If not possible, dispose the old enumerator and create a new one from the // next response, if any. _singleResponseRowEnumerator?.Dispose(); if (await _stream.ResponseStream.MoveNext(cancellationToken).ConfigureAwait(false)) { cancellationToken.ThrowIfCancellationRequested(); _singleResponseRowEnumerator = MergeResponseChunks(_stream.ResponseStream.Current); } else { _singleResponseRowEnumerator = null; break; } } // If the current response's enumerator has produced a row, this enumerator should produce // that same row. if (_singleResponseRowEnumerator != null) { cancellationToken.ThrowIfCancellationRequested(); Current = _singleResponseRowEnumerator.Current; return(true); } if (IsRowInProgress && _stream.GrpcCall?.GetStatus().StatusCode != StatusCode.Cancelled) { throw new InvalidOperationException("The ReadRows stream has ended with a row in progress."); } return(false); IEnumerator <Row> MergeResponseChunks(ReadRowsResponse response) { foreach (var chunk in response.Chunks) { if (chunk.ResetRow) { Assert( (chunk.RowKey == null || chunk.RowKey.IsEmpty) && chunk.FamilyName == null && chunk.Qualifier == null && (chunk.Value == null || chunk.Value.IsEmpty) && chunk.TimestampMicros == 0, "A reset should have no data"); Assert(_rowMergeState != NewRow.Instance, "NewRow must have a rowKey"); Reset(); continue; } _rowMergeState = _rowMergeState.HandleChunk(this, chunk); if (chunk.CommitRow) { _rowMergeState = _rowMergeState.CommitRow(this); // TODO: Capture response.LastScannedRowKey here for smart retries yield return(_currentCell.Row); } } } }