Skip to content

fix(storage): honor timeout in upload_chunks_concurrently - #18765

Open
abhishekacharya200 wants to merge 1 commit into
googleapis:mainfrom
abhishekacharya200:fix/storage-upload-chunks-timeout
Open

abhishekacharya200 wants to merge 1 commit into
googleapis:mainfrom
abhishekacharya200:fix/storage-upload-chunks-timeout

Conversation

@abhishekacharya200

Copy link
Copy Markdown
Contributor

Fixes #18764

upload_chunks_concurrently takes a timeout argument and documents it as the time to wait for the server response, but it never passed it on. The XML multipart requests it makes (initiate, each part upload, finalize, and cancel when something fails) all ran with the (61, 60) default of the media layer, so a caller could not make a stuck upload fail sooner or give a slow connection more time.

This passes timeout to XMLMPUContainer.initiate, finalize and cancel, and adds it to the _upload_part worker so every XMLMPUPart.upload gets it as well. Thread and process workers behave the same, since the timeout is a plain number or tuple.

One small change for callers who don't pass a timeout: these requests now use the documented default of 60 seconds (_DEFAULT_TIMEOUT, the value every Blob upload and download already sends) instead of (61, 60). Only the connect timeout moves, by one second.

The cleanup cancel gets the same timeout as the other requests, so with a very short timeout the cancel itself can time out too. I kept it consistent with the rest, but I'm happy to leave cancel on the default if you'd rather keep the cleanup more forgiving.

With the script from the issue, which records the timeout each request gets:

main                            this branch
initiate     timeout=(61, 60)   initiate     timeout=5
upload part  timeout=(61, 60)   upload part  timeout=5
upload part  timeout=(61, 60)   upload part  timeout=5
finalize     timeout=(61, 60)   finalize     timeout=5

Added test_upload_chunks_concurrently_passes_timeout and test_upload_chunks_concurrently_passes_timeout_to_cancel, which fail on main and pass with this change. The existing tests that checked these calls now also check that the default timeout is sent, and test__upload_part checks that the worker hands its timeout to the part upload. The full tests/unit suite passes locally (2068 passed), and the noxfile's ruff==0.14.14 import and format checks and flake8 report nothing.

upload_chunks_concurrently accepted a timeout but never passed it to the
XML multipart requests, so initiate, every part upload, finalize and
cancel always ran with the (61, 60) default. Pass it through to all of
them, including the _upload_part worker.

Callers who don't pass a timeout now get the documented 60 seconds
(_DEFAULT_TIMEOUT, as every Blob upload and download already sends)
instead of (61, 60), so only the connect timeout moves by one second.

Fixes googleapis#18764
@abhishekacharya200
abhishekacharya200 requested review from a team as code owners October 19, 2026 16:47

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request propagates the timeout parameter to various container and part operations within upload_chunks_concurrently (such as initiate, finalize, cancel, and _upload_part) and adds corresponding unit tests. The review feedback recommends wrapping the container.cancel call inside the exception handler in a nested try...except block to prevent cancellation failures from masking the original exception.

Comment on lines 1246 to 1248
except Exception:
container.cancel(blob._get_transport(client))
container.cancel(blob._get_transport(client), timeout=timeout)
raise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

If container.cancel raises an exception (for example, due to a timeout or network issue during the cleanup attempt), it will mask the original exception that triggered the except block. To ensure the original error is preserved and propagated to the caller for proper debugging, wrap the container.cancel call in a nested try...except block. Avoid silently ignoring the exception with a broad except Exception: pass block; instead, log the exception using logger.warning to aid in debugging and prevent masking underlying issues.

    except Exception:
        try:
            container.cancel(blob._get_transport(client), timeout=timeout)
        except Exception as e:
            logger.warning("Failed to cancel container: %s", e)
        raise
References
  1. Avoid broad except Exception: blocks that silently return None. Instead, log the exception (e.g., using logger.warning) to aid in debugging and prevent masking underlying issues.

This branch has not been deployed

No deployments
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.

storage: upload_chunks_concurrently ignores its timeout argument

2 participants