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 storage.upload(path) #269

Merged
merged 14 commits into from Jun 25, 2020
Merged

Conversation

dmitry-fa
Copy link
Contributor

Fixes #179

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Apr 17, 2020
@dmitry-fa dmitry-fa requested review from frankyn and elharo and removed request for frankyn April 17, 2020 14:08
@codecov
Copy link

codecov bot commented Apr 17, 2020

Codecov Report

Merging #269 into master will increase coverage by 0.06%.
The diff coverage is 68.42%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master     #269      +/-   ##
============================================
+ Coverage     63.13%   63.19%   +0.06%     
- Complexity      601      609       +8     
============================================
  Files            32       32              
  Lines          5078     5111      +33     
  Branches        481      485       +4     
============================================
+ Hits           3206     3230      +24     
- Misses         1708     1717       +9     
  Partials        164      164              
Impacted Files Coverage Δ Complexity Δ
...rc/main/java/com/google/cloud/storage/Storage.java 80.90% <ø> (ø) 0.00 <0.00> (ø)
...om/google/cloud/storage/spi/v1/HttpStorageRpc.java 1.50% <0.00%> (-0.02%) 1.00 <0.00> (ø)
...va/com/google/cloud/storage/spi/v1/StorageRpc.java 64.86% <ø> (ø) 0.00 <0.00> (ø)
...ogle/cloud/storage/testing/StorageRpcTestBase.java 96.00% <0.00%> (-1.96%) 48.00 <0.00> (ø)
...ava/com/google/cloud/storage/BlobWriteChannel.java 74.60% <100.00%> (+2.93%) 10.00 <1.00> (+1.00)
...ain/java/com/google/cloud/storage/StorageImpl.java 81.00% <100.00%> (+0.47%) 138.00 <7.00> (+7.00)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 37f00eb...d7c7f1b. Read the comment docs.


@Test
public void testUploadDirectory() throws IOException {
EasyMock.replay(storageRpcMock);
Copy link
Contributor

Choose a reason for hiding this comment

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

Please use Mockito for all new tests. It really is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@elharo I was going to use Mockito for these new tests, but found this idea not very productive.
I submitted #270 and assigned to myself to fix 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.

@elharo, new tests for upload() converted to mockito.
It's not a final version yet. upload() method will be updated to return Blob (now they return void), that will require test update as well.

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.

1 nit so far, added @JesseLovelace for surface review as well.

* @throws StorageException on failure
* @see #upload(BlobInfo, Path, int, BlobWriteOption...)
*/
void upload(BlobInfo blobInfo, Path path, BlobWriteOption... options) throws IOException;
Copy link
Member

Choose a reason for hiding this comment

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

Blob should be returned from upload() methods similar to create() methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

returning Blob requires an extra RPC request to be issued, that is not always needed.
I can add uploadFrom() method to the Blob class (symmetric to downloadTo) to return an updated Blob.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

A month ago I compared upload/download implemented in various ways for several types of file. This is the summary

Copy link
Member

Choose a reason for hiding this comment

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

Thanks @dmitry-fa,

I was thinking about the issue you filed: #149

When the upload is complete GCS responds with object metadata so we don't need to perform an additional get request. flushBuffer doesn't return a value so we'd diverge from the spec a bit to get the metadata.

Copy link
Contributor Author

@dmitry-fa dmitry-fa Apr 18, 2020

Choose a reason for hiding this comment

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

Do you mean we can update the StorageRpc interface to make void write(uploadId, ...) to return a StorageObject?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

1 nit so far, added @JesseLovelace for surface review as well.
Blob should be returned from upload() methods similar to create() methods.

It's not a nit at all :) It's a significant change. I agree with you, upload() should return Blob.
I see the following ways how to implement:

  1. Update the StorageRpc interface and make write() to return a StorageObject instance or null in write is not final request for the given upload. Now StorageRpc .write() returns null.

  2. Update HttpStorageRpc.write() to remember StorageObject and return this object in a separate method.
    In this case BlobWriterChannel.fluchBuffer() will look like:

public void run() {
         StorageRpc  rpc = getOptions().getStorageRpcV1();
         rpc.write(getUploadId(), getBuffer(), 0, getPosition(), length, last);
         if (last && rpc instnceof HttpStorageRpc) {
             this.uploadedBlob = ((HttpStorageRpc)rpc.getUplodedBlob())
         } else {
             this.uploadedBlob = null;  // let storage.upload() to resolve it
         }
}
  1. Issue an extra rpc per upload to return a Blob

I believe 2. is not a Google way, so we should go 1. As a temporary solution 3. is also considered, because it could be improved to 1.

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hi @frankyn,

When the upload is complete GCS responds with object metadata so we don't need to perform an additional get request.

I managed to implement upload based on your hint. I modified StorageRpc.write() to return StorageObject instead of void.
Two issues were found:

  • GCS does not respond with object metadata if writer was created by storage.writer(signedUrl)
    Not sure if it's a bug or a feature, looks like a bug: spec says nothing about it. I worked it around.

  • Change StrorageRpc affects storage-nio, Kokoro - Test: Linkage Monitor fails:
    (com.google.cloud:google-cloud-nio:0.120.0-alpha) com.google.cloud.storage.contrib.nio.testing.FakeStorageRpc's method write(String arg1, byte[] arg2, int arg3, long arg4, int arg6, boolean arg7) is not implemented in the class referenced from com.google.cloud.storage.spi.v1.StorageRpc (com.google.cloud:google-cloud-storage:1.107.1-SNAPSHOT)

cc: @JesseLovelace

* @throws StorageException on failure
* @see #upload(BlobInfo, Path, int, BlobWriteOption...)
*/
void upload(BlobInfo blobInfo, Path path, BlobWriteOption... options) throws IOException;
Copy link
Member

Choose a reason for hiding this comment

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

Thanks @dmitry-fa,

I was thinking about the issue you filed: #149

When the upload is complete GCS responds with object metadata so we don't need to perform an additional get request. flushBuffer doesn't return a value so we'd diverge from the spec a bit to get the metadata.

@googlebot
Copy link

We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for all the commit author(s) or Co-authors. If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google.
In order to pass this check, please resolve this problem and then comment @googlebot I fixed it.. If the bot doesn't comment, it means it doesn't think anything has changed.

ℹ️ Googlers: Go here for more info.

@googlebot googlebot added cla: no This human has *not* signed the Contributor License Agreement. and removed cla: yes This human has signed the Contributor License Agreement. labels Apr 21, 2020
@googlebot
Copy link

CLAs look good, thanks!

ℹ️ Googlers: Go here for more info.

@googlebot googlebot added cla: yes This human has signed the Contributor License Agreement. and removed cla: no This human has *not* signed the Contributor License Agreement. labels Apr 21, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 27, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Apr 27, 2020
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.

Thanks @dmitry-fa for your patience.

The more I think about this you may want to use StorageRpc.open() and StorageRpc.write() directly instead of through the writer.

That is an alternative solution there, but it does introduce duplication of code. I'm not convinced that there a clean solution other than what you've done by introducing a new value of objectProto.

As for the linkagefailure, it's known and I'm working on addressing it in #242. Need to address the split in resources here and I'm still working on it.

<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
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 split out mockito tests into a separate PR because it got a bit confusing in review? Apologies for churn on 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.

Agree. Done #284

@dmitry-fa
Copy link
Contributor Author

Hi @frankyn,

Thanks for you comments.

The more I think about this you may want to use StorageRpc.open() and StorageRpc.write() directly instead of through the writer.

That is an alternative solution there, but it does introduce duplication of code. I'm not convinced that there a clean solution other than what you've done by introducing a new value of objectProto.

I agree, objectProto is not elegant solution, but it's better than duplication of BaseWriteChannel code. BaseWriteChannel provides nice and not trivial functionality on buffering (setChunkSize()) which is already tested. Repeating that may lead to new bugs.

@frankyn
Copy link
Member

frankyn commented Apr 28, 2020

Thanks @dmitry-fa, let's move forward for now. It's not introducing new surface methods in the BlobWriteChannel class. Please resolve merge conflicts. This will be blocked until Linkage Monitor is resolved because the StorageRpc update.

@dmitry-fa
Copy link
Contributor Author

Hi @frankyn,

thanks for the prompt response. I have one more question regarding this feature.
Once we add upload() methods to the Storage there will be two groups of methods with similar signatures which do the same job: create() and upload():

  @Deprecated
  Blob create(BlobInfo blobInfo, InputStream content, BlobWriteOption... options);

  Blob upload(BlobInfo blobInfo, InputStream content, BlobWriteOption... options) throws IOException;

The differences between them:

  • create() use direct upload, upload() use resumable upload. Would it make sense to reflect this
  • upload() throw IOException, create() throw StorageException

Would it make sense to use create instead of upload to reflect this?

# Conflicts:
#	google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java
#	pom.xml
# Conflicts:
#	google-cloud-storage/clirr-ignored-differences.xml
#	google-cloud-storage/src/main/java/com/google/cloud/storage/StorageImpl.java
#	google-cloud-storage/src/test/java/com/google/cloud/storage/StorageImplMockitoTest.java
#	google-cloud-storage/src/test/java/com/google/cloud/storage/it/ITStorageTest.java
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 18, 2020
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
@dmitry-fa dmitry-fa requested a review from frankyn June 19, 2020 07:58
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 19, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 19, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 20, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 20, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 22, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 22, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 23, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 23, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 24, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 24, 2020
@dmitry-fa dmitry-fa added kokoro:force-run Add this label to force Kokoro to re-run the tests. and removed kokoro:force-run Add this label to force Kokoro to re-run the tests. labels Jun 24, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 25, 2020
@dmitry-fa dmitry-fa added the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 25, 2020
@yoshi-kokoro yoshi-kokoro removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Jun 25, 2020
@frankyn
Copy link
Member

frankyn commented Jun 25, 2020

@dmitry-fa could you address the conflict added after merging #138 PR?

@dmitry-fa
Copy link
Contributor Author

@frankyn I think it's not needed to be merged.

@frankyn frankyn merged commit 9457f3a into googleapis:master Jun 25, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

add Storage.upload(Path)
5 participants