Skip to content
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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

BlobWriter should abort multipart upload during exception handling #1228

Open
ddelange opened this issue Feb 21, 2024 · 7 comments 路 May be fixed by #1243
Open

BlobWriter should abort multipart upload during exception handling #1228

ddelange opened this issue Feb 21, 2024 · 7 comments 路 May be fixed by #1243
Assignees
Labels
api: storage Issues related to the googleapis/python-storage API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.

Comments

@ddelange
Copy link
Contributor

ddelange commented Feb 21, 2024

Hi 馃憢

When an error occurs during a multipart upload, BlobWriter.__exit__ is called with exception context. When that is the case, the multipart upload should be aborted. Currently, BlobWriter.__exit__ blindly calls BlobWriter.close, which in turn finishes (commits) the multipart upload. The user then ends up with a partial blob on gcs, whereas the multipart upload should have been aborted.

Steps to reproduce

  • init a BlobWriter
  • write some bytes that are to be uploaded
  • raise an error that should result in aborting the upload

Code example

with blob.open('wb') as fp:
    fp.write(b'first chunk')  # not yet uploaded
    fp.write(b'big chunk' * 1024 ** 8)  # uploaded
    raise ValueError('SIGTERM received')

# the new blob should not have been created on gcs

For reference: smart_open.s3.MultiPartWriter.__exit__

@product-auto-label product-auto-label bot added the api: storage Issues related to the googleapis/python-storage API. label Feb 21, 2024
@ddelange ddelange changed the title BlobWriter commits upload during exception handling BlobWriter should abort multipart upload during exception handling Feb 21, 2024
@frankyn frankyn self-assigned this Feb 22, 2024
@frankyn frankyn added type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns. priority: p2 Moderately-important priority. Fix may not be included in next release. labels Feb 22, 2024
@frankyn
Copy link
Member

frankyn commented Feb 22, 2024

Hi @ddelange, thanks for the context I was able to reproduce the failure.

From some initial prototyping I think we can resolve this by adding __exit__() to BlobWriter.
What I did was close the underlying buffer if there's an exception which makes the close() call a no-op.

exit() example:

def __exit__(self, exc_type, exc_val, traceback):
      if exc_type != None:
        self._buffer.close()
        return False
      return True

Full repro example:

from google.cloud import storage
import http, time
http.client.HTTPConnection.debuglevel=5 # used to review outgoing requests
storage_client = storage.Client()
bucket = storage_client.bucket("bucket")
blob = bucket.blob("test-upload-1228")


with blob.open('wb', chunk_size=1024*1024*2) as fp:
  fp.write(b'first chunk') # not yet uploaded
  fp.write(b'b' * (1024 * 1024 * 4)) # uploaded
  raise BaseException("error")

@andrewsg or @cojenco can one of you work to address this issue in fileio?

@ddelange
Copy link
Contributor Author

nice! why do you wish to return True in the no-exception case? I think __exit__ here can just implicitly return None? ref https://stackoverflow.com/a/43946444/5511061 returning True only if you explicitly want to suppress exc and prevent it from propagating

@frankyn
Copy link
Member

frankyn commented Feb 23, 2024

nice! why do you wish to return True in the no-exception case? I think exit here can just implicitly return None? ref https://stackoverflow.com/a/43946444/5511061 returning True only if you explicitly want to suppress exc and prevent it from propagating

Primarily a prototyping miss. thanks for pointing that out.

After an offline discussion this is a tricky issue in that the feature has been implemented this way for several years and other users would have adopted the existing pattern (complete whatever is written to GCS).

@cojenco @andrewsg An alternative would be to use a flag; that fails the object upload on failure unless a failure should cancel out the upload. I naively thought the latter but there are likely consequences in changing this behavior now.

@ddelange
Copy link
Contributor Author

Does gcs employ some sort of deprecation warning system to phase in such behaviour changes?

To make the old behaviour opt-in, and flip that default in the next major version?

From the Zen of Python:

Errors should never pass silently.
Unless explicitly silenced.

I would argue that making the proper behaviour of the with-statement opt-in would be the wrong way around. People exploiting bugs can reasonably expect that bug to be fixed at some point.

@ddelange
Copy link
Contributor Author

Hi 馃憢

I've opened a PR to cancel the resumable upload analogous to the implementations in smart_open for azure and s3 (and previously gcs).

Please have a look, especially with the feature flag topic above still open for now.

@ddelange
Copy link
Contributor Author

Hi 馃憢

Did you happen to discuss #1243 internally in the meantime?

@andrewsg
Copy link
Contributor

We did discuss it. I would prefer the behavior you suggest. We do have concerns that it may break applications for anyone who has worked around the existing behavior. We will definitely incorporate this fix into the upcoming major version release. Whether we incorporate it into a minor version release before that is still undecided. Thank you very much for your kind attention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api: storage Issues related to the googleapis/python-storage API. priority: p2 Moderately-important priority. Fix may not be included in next release. type: bug Error or flaw in code with unintended results or allowing sub-optimal usage patterns.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants