Skip to content

Retry stream read errors in SimpleDownloader#581

Open
agoncharov-reef wants to merge 7 commits into
Backblaze:masterfrom
reef-technologies:retry-on-mid-stream-read-errors
Open

Retry stream read errors in SimpleDownloader#581
agoncharov-reef wants to merge 7 commits into
Backblaze:masterfrom
reef-technologies:retry-on-mid-stream-read-errors

Conversation

@agoncharov-reef

Copy link
Copy Markdown
Contributor

SimpleDownloader already had range-based retry logic for truncated downloads (e.g. when the server closes the connection mid-stream), but ChunkedEncodingError, ConnectionError, and ContentDecodingError raised during iter_content bypassed that path and failed the download immediately.

This PR catches those exceptions during both the initial read and follow-up range requests, logs them at debug level, and lets the existing retry loop resume. Retry is skipped when content is decoded (content_encoding set and decode_content=True), since a partially decoded stream cannot be resumed with byte ranges.

Also fixes v1.B2Api not exposing api_config, and ensures decoded downloads still get truncation checks after download (hash verification remains skipped for them).

and download_version.content_encoding
and download_version.api.api_config.decode_content
):
if not self.SUPPORTS_DECODE_CONTENT and download_version._should_be_decoded:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

_should_be_decoded is private and should not be accessed from outside. Rename to should_be_decoded?

# or something and the server closes connection, while neither tcp or http have a problem
# with the truncated output, so we detect it here and try to continue

num_tries = 5 # this is hardcoded because we are going to replace the entire retry interface soon, so we'll avoid deprecation here and keep it private

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I don't think you should un-hardcode it here as when this will turn into an interface that folks can rely on, it will be difficult to maintain the interface over the refactoring. Move to module scoped _MAX_RETRIES?

return parse_http_date(self.expires)

@property
def _should_be_decoded(self) -> bool:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

if the rest of the code expects DownloadVersion-like objects to have this method or else it's going to crash, then we have to add it to interface, meaning we have to document it so that those who inherit will know what it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants