Skip to content

feat: retry upon ChunkedEncodingError#994

Open
zhijie-yang wants to merge 2 commits intocanonical:mainfrom
zhijie-yang:retry-in-download-chunks
Open

feat: retry upon ChunkedEncodingError#994
zhijie-yang wants to merge 2 commits intocanonical:mainfrom
zhijie-yang:retry-in-download-chunks

Conversation

@zhijie-yang
Copy link
Contributor

@zhijie-yang zhijie-yang commented Jan 25, 2026

  • Have you followed the guidelines for contributing?
  • Have you signed the CLA?
  • Have you successfully run make lint && make test?
  • Have you added an entry to the changelog (docs/reference/changelog.rst)?

Resolved #993

@zhijie-yang zhijie-yang force-pushed the retry-in-download-chunks branch from 7b80915 to c576e34 Compare January 25, 2026 19:28
@zhijie-yang zhijie-yang marked this pull request as ready for review January 25, 2026 21:37
@zhijie-yang zhijie-yang requested a review from a team as a code owner January 25, 2026 21:37
Copilot AI review requested due to automatic review settings January 25, 2026 21:37
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds retry logic to handle ChunkedEncodingError exceptions during file downloads, implementing automatic retries with progress tracking.

Changes:

  • Added retry mechanism with configurable max attempts (3) for download operations
  • Implemented progress tracking and rollback functionality for interrupted downloads
  • Added comprehensive test coverage for both successful retry scenarios and exhausted retry scenarios

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
craft_application/services/request.py Implements retry logic with progress tracking and rollback for ChunkedEncodingError
tests/unit/services/test_request.py Adds test cases for retry success and retry exhaustion scenarios

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

assert path.read_bytes() == downloads[url]


def failing_iter_content(chunk_size=None): # pylint: disable=unused-argument
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The function name 'failing_iter_content' could be more descriptive. Consider renaming to 'simulate_chunked_encoding_error_in_iter_content' to better convey its purpose as a test helper.

Copilot uses AI. Check for mistakes.

# Patch the get method to simulate ChunkedEncodingError on first attempts
original_get = request_service.get
call_count = {"count": 0}
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

Using a dictionary to track a simple counter is unnecessary. Consider using a list with a single element (e.g., call_count = [0]) or a nonlocal variable for cleaner code.

Copilot uses AI. Check for mistakes.
super().__init__(app, services)
self._session = requests.Session()
self._session.headers["User-Agent"] = f"{self._app.name}/{self._app.version}"
self._max_retries = 3
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The max_retries value is hardcoded. Consider making it configurable via constructor parameter or a class constant to improve flexibility and testability.

Copilot uses AI. Check for mistakes.
Comment on lines +94 to +95
# Yield negative sum of chunk sizes to indicate rollback
yield -downloaded_bytes
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The rollback mechanism using negative values is not documented in the function's docstring. This behavior should be documented as it's part of the public API contract that callers need to understand.

Copilot uses AI. Check for mistakes.
except requests.exceptions.ChunkedEncodingError:
if attempt < self._max_retries - 1:
craft_cli.emit.progress(
f"Download interrupted, retrying... (attempt {attempt + 1}/{self._max_retries})"
Copy link

Copilot AI Jan 25, 2026

Choose a reason for hiding this comment

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

The retry attempt numbering is confusing. When displaying 'attempt 1/3', this is actually the second attempt (first retry). Consider changing to 'retry {attempt + 1} of {self._max_retries - 1}' to accurately reflect retry count rather than total attempts.

Suggested change
f"Download interrupted, retrying... (attempt {attempt + 1}/{self._max_retries})"
f"Download interrupted, retrying... (retry {attempt + 1} of {self._max_retries - 1})"

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@mr-cal mr-cal left a comment

Choose a reason for hiding this comment

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

Thanks for the PR @zhijie-yang. Can you use craft-application's retry utility to accomplish this?

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.

request should be able to retry upon broken connections

3 participants