-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
Open Write #13344
Open Write #13344
Conversation
@PaulVrugt, we are continuing Open Write here. |
Cool, thanks for the heads up! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly nitpicking, but there are some potential bugs to be addressed. Also some questions on tiny design elements (nothing major).
Will review tests when they are completed.
sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
Outdated
Show resolved
Hide resolved
sdk/storage/Azure.Storage.Files.Shares/src/ShareFileWriteStream.cs
Outdated
Show resolved
Hide resolved
A lot of this logic is handled in the implementation streams, I'm not sure how valuable having tests just for StorageWriteStream will be. |
sdk/storage/Azure.Storage.Common/tests/StorageWriteStreamTests.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the Java implementation share the exact semantics around which conditions are used, updated, and cleared at various points in the life cycle? What about Track 1?
sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
Show resolved
Hide resolved
sdk/storage/Azure.Storage.Blobs/api/Azure.Storage.Blobs.netstandard2.0.cs
Outdated
Show resolved
Hide resolved
#pragma warning restore AZC0015 // Unexpected client method return type. | ||
bool overwrite, | ||
long position, | ||
long? size = default, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose to add an optional size parameter, and mentioned in the xml comments it is required if overwrite is set to true, or the Page Blob is being created for the first time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move at least size
and maybe position
(if it can default to 0
when creating a new page blob) into PageBlobOpenWriteOptions
. Having optional params in both places ends up being a little confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved Size
to PageBlobOpenWriteOptions
. I'd like to keep position
a required parameter to prevent users from overwriting the data at the beginning of a Page Blob by default.
private async Task<Stream> OpenWriteInternal( | ||
bool overwrite, | ||
long position, | ||
long? maxSize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose to add an optional maxSize parameter, and mentioned in the xml comments it is required if overwrite is set to true, or the File is being created for the first time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but I think we should still move it to the options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in next iteration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome progress - I think there are just a couple of other spots we need to update the etag.
_conditions.IfAppendPositionEqual = null; | ||
_conditions.IfMaxSizeLessThanOrEqual = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would expect these lines get removed now that we're splitting the concepts of OpenConditions and WriteConditions?
#pragma warning restore AZC0015 // Unexpected client method return type. | ||
bool overwrite, | ||
long position, | ||
long? size = default, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should move at least size
and maybe position
(if it can default to 0
when creating a new page blob) into PageBlobOpenWriteOptions
. Having optional params in both places ends up being a little confusing.
// We need a multiple of 512 to flush. | ||
if (_buffer.Length % Constants.Blob.Page.PageSizeBytes != 0) | ||
{ | ||
int bytesToWrite = (int)(Constants.Blob.Page.PageSizeBytes - _buffer.Length % Constants.Blob.Page.PageSizeBytes); | ||
await WriteToBufferInternal(buffer, offset, bytesToWrite, async, cancellationToken).ConfigureAwait(false); | ||
remaining -= bytesToWrite; | ||
offset += bytesToWrite; | ||
} | ||
|
||
// Flush the buffer. | ||
await AppendInternal(async, cancellationToken).ConfigureAwait(false); | ||
|
||
while (remaining > 0) | ||
{ | ||
await WriteToBufferInternal( | ||
buffer, | ||
offset, | ||
(int)Math.Min(remaining, _bufferSize), | ||
async, | ||
cancellationToken).ConfigureAwait(false); | ||
|
||
// Remaining bytes won't fit in buffer. | ||
if (remaining > _bufferSize) | ||
{ | ||
await AppendInternal(async, cancellationToken).ConfigureAwait(false); | ||
remaining -= (int)_bufferSize; | ||
offset += (int)_bufferSize; | ||
} | ||
|
||
// Remaining bytes will fit in buffer. | ||
else | ||
{ | ||
remaining = 0; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you need the same 512 alignment on the Write/Append calls in the loop?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, because the number of bytes we are writing to the buffer is (int)Math.Min(remaining, _bufferSize),
. In the case remaining > _bufferSize
, we just wrote _bufferSize
to the buffer, which is an increment of 512.
|
||
if (!overwrite) | ||
{ | ||
throw new ArgumentException($"{nameof(BlockBlobClient)}.{nameof(BlockBlobClient.OpenWrite)} only supports overwrite."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should include the overwrite
name as another param to ArgEx. We should also maybe change the message to "only supports overwriting" to make it slightly clearer it's the specific value of the param and not just using the param with any value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to "only supports overwriting".
async: async, | ||
cancellationToken: cancellationToken) | ||
.ConfigureAwait(false); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
https://docs.microsoft.com/en-us/rest/api/storageservices/put-block-list says:
You can call Put Block List to update a blob by uploading only those blocks that have changed, then committing the new and existing blocks together. You can do this by specifying whether to commit a block from the committed block list or from the uncommitted block list, or to commit the most recently uploaded version of the block, whichever list it may belong to.
private async Task<Stream> OpenWriteInternal( | ||
bool overwrite, | ||
long position, | ||
long? maxSize, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense, but I think we should still move it to the options
if (async) | ||
{ | ||
await _fileClient.UploadRangeAsync( | ||
range: httpRange, | ||
content: _buffer, | ||
progressHandler: _progressHandler, | ||
conditions: _conditions, | ||
cancellationToken: cancellationToken) | ||
.ConfigureAwait(false); | ||
} | ||
else | ||
{ | ||
_fileClient.UploadRange( | ||
range: httpRange, | ||
content: _buffer, | ||
progressHandler: _progressHandler, | ||
conditions: _conditions, | ||
cancellationToken: cancellationToken); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't these need to update the etag?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Files Service doesn't support ETags.
|
Fixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Thanks Sean!
Guys stupid question, is this functionality released? I can't find the OpenWrite method on the BlobClient. |
It's not present on the |
Continued from #12779 and #12662