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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add headers option to MPU #2303

Merged
merged 5 commits into from
Sep 21, 2023
Merged

Conversation

ddelgrosso1
Copy link
Contributor

Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Make sure to open an issue as a bug/issue before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea
  • Ensure the tests and linter pass
  • Code coverage does not decrease (if any source code was changed)
  • Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕

@ddelgrosso1 ddelgrosso1 requested review from a team as code owners September 14, 2023 17:18
@product-auto-label product-auto-label bot added size: l Pull request size is large. api: storage Issues related to the googleapis/nodejs-storage API. labels Sep 14, 2023
@ddelgrosso1 ddelgrosso1 added owlbot:run Add this label to trigger the Owlbot post processor. and removed api: storage Issues related to the googleapis/nodejs-storage API. labels Sep 14, 2023
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Sep 14, 2023
@ddelgrosso1
Copy link
Contributor Author

This looks larger than it really is due to the fact that I removed proxyquire and all the fake objects from the transfer manager tests.

Comment on lines -46 to -74
const fakeUtil = Object.assign({}, util);
fakeUtil.noop = util.noop;

class FakeServiceObject extends ServiceObject<FakeServiceObject, BaseMetadata> {
calledWith_: IArguments;
constructor(config: ServiceObjectConfig) {
super(config);
// eslint-disable-next-line prefer-rest-params
this.calledWith_ = arguments;
}
}

class FakeAcl {
calledWith_: Array<{}>;
constructor(...args: Array<{}>) {
this.calledWith_ = args;
}
}

class FakeFile {
calledWith_: IArguments;
bucket: Bucket;
name: string;
options: FileOptions;
metadata: FileMetadata;
createWriteStream: Function;
isSameFile = () => false;
constructor(bucket: Bucket, name: string, options?: FileOptions) {
// eslint-disable-next-line prefer-rest-params
Copy link
Contributor

Choose a reason for hiding this comment

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

My favorite part of the PR 💣

@product-auto-label product-auto-label bot added the api: storage Issues related to the googleapis/nodejs-storage API. label Sep 15, 2023
@@ -664,8 +696,12 @@ export class TransferManager {
let partNumber = 1;
let promises: Promise<void>[] = [];
try {
if (options.abortExisting && options.uploadId) {
Copy link

@danielduhh danielduhh Sep 15, 2023

Choose a reason for hiding this comment

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

How does this option work? If I set abortExisting to true, and there's an upload ID, it will cancel the the upload? Shouldn't it only do this if it fails?

Copy link
Member

Choose a reason for hiding this comment

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

oh, looks like you can abort an upload using uploadFileInChunks when you set abortExisting=True and uploadId is set; based on

[abortExisting] boolean to indicate if an in progress upload should be aborted. uploadID must also be supplied in order to abort the upload.

I guess if this delete in context of this method; the upload id would be known and isn't required like this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@danielduhh correct the user would have to pass back the uploadId and set the abortExisting option. The way it works currently is if there is a failure the return has the uploadId and mapping of the completed parts that were uploaded.

);
});

it('should call abortUpload when passed the option and uploadID', async () => {

Choose a reason for hiding this comment

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

This seems like a hack? Why not just expose the abort API and ask for an id? We could document that users should catch failures and call this method (since we're not doing it for them)/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When we implemented this we decided not to expose the actual XML implementation to avoid users accessing it directly. This was done because at the time we did not know if other MPU implementations would come along and the default would be changed (i.e. JSON).

Choose a reason for hiding this comment

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

Yeah it feels odd to have this API do two completely different things based on the parameters.
Instead, I would consider following python and catching any errors thrown by MPU with a note:

The library will attempt to cancel uploads that fail due to an exception.
    If the upload fails in a way that precludes cancellation, such as a
    hardware failure, process termination, or power outage, then the incomplete
    upload may persist indefinitely. To mitigate this, set the
    `AbortIncompleteMultipartUpload` with a nonzero `Age` in bucket lifecycle
    rules, or refer to the XML API documentation linked above to learn more
    about how to list and delete individual downloads.
    ``` 

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This goes back to the debate of autodelete vs not. If we want to proceed with autodelete, I'll just move the logic internally to call the delete function.

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I set up a discussion with @andrewsg and @ddelgrosso1 to suss this out today; will report back;

*
* @returns {Promise<void>}
*/
async abortUpload(): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

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

Can you make this private?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This entire XML implementation is not exported and therefore not accessible.

@@ -664,8 +696,12 @@ export class TransferManager {
let partNumber = 1;
let promises: Promise<void>[] = [];
try {
if (options.abortExisting && options.uploadId) {
Copy link
Member

Choose a reason for hiding this comment

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

oh, looks like you can abort an upload using uploadFileInChunks when you set abortExisting=True and uploadId is set; based on

[abortExisting] boolean to indicate if an in progress upload should be aborted. uploadID must also be supplied in order to abort the upload.

I guess if this delete in context of this method; the upload id would be known and isn't required like this.

@ddelgrosso1 ddelgrosso1 added the owlbot:run Add this label to trigger the Owlbot post processor. label Sep 19, 2023
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Sep 19, 2023
Copy link
Member

@frankyn frankyn left a comment

Choose a reason for hiding this comment

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

one nit;

* @property {object} [headers] headers to be sent when initiating the multipart upload.
* See {@link https://cloud.google.com/storage/docs/xml-api/post-object-multipart#request_headers| Request Headers: Initiate a Multipart Upload}
* @property {boolean} [autoAbortFailure] boolean to indicate if an in progress upload should be aborted automatically upon failure. If not set,
* failures will be automatically aborted.
Copy link
Member

Choose a reason for hiding this comment

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

upload session will be automatically aborted

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated, thanks @frankyn

Copy link

@danielduhh danielduhh left a comment

Choose a reason for hiding this comment

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

Nice!

@ddelgrosso1 ddelgrosso1 added the owlbot:run Add this label to trigger the Owlbot post processor. label Sep 19, 2023
@gcf-owl-bot gcf-owl-bot bot removed the owlbot:run Add this label to trigger the Owlbot post processor. label Sep 19, 2023
@ddelgrosso1 ddelgrosso1 merged commit 7f58f30 into googleapis:main Sep 21, 2023
14 checks passed
@ddelgrosso1 ddelgrosso1 deleted the tm-headers branch September 21, 2023 17:48
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/nodejs-storage API. size: l Pull request size is large.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants