@@ -66,7 +66,7 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
6666 if o .BlockSize == 0 {
6767 // If bufferSize > (BlockBlobMaxStageBlockBytes * BlockBlobMaxBlocks), then error
6868 if bufferSize > BlockBlobMaxStageBlockBytes * BlockBlobMaxBlocks {
69- return nil , errors .New ("Buffer is too large to upload to a block blob" )
69+ return nil , errors .New ("buffer is too large to upload to a block blob" )
7070 }
7171 // If bufferSize <= BlockBlobMaxUploadBlobBytes, then Upload should be used with just 1 I/O request
7272 if bufferSize <= BlockBlobMaxUploadBlobBytes {
@@ -76,7 +76,7 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
7676 if o .BlockSize < BlobDefaultDownloadBlockSize { // If the block size is smaller than 4MB, round up to 4MB
7777 o .BlockSize = BlobDefaultDownloadBlockSize
7878 }
79- // StageBlock will be called with blockSize blocks and a parallelism of (BufferSize / BlockSize).
79+ // StageBlock will be called with blockSize blocks and a Parallelism of (BufferSize / BlockSize).
8080 }
8181 }
8282
@@ -95,12 +95,12 @@ func UploadBufferToBlockBlob(ctx context.Context, b []byte,
9595 progress := int64 (0 )
9696 progressLock := & sync.Mutex {}
9797
98- err := doBatchTransfer (ctx , batchTransferOptions {
99- operationName : "UploadBufferToBlockBlob" ,
100- transferSize : bufferSize ,
101- chunkSize : o .BlockSize ,
102- parallelism : o .Parallelism ,
103- operation : func (offset int64 , count int64 ) error {
98+ err := DoBatchTransfer (ctx , BatchTransferOptions {
99+ OperationName : "UploadBufferToBlockBlob" ,
100+ TransferSize : bufferSize ,
101+ ChunkSize : o .BlockSize ,
102+ Parallelism : o .Parallelism ,
103+ Operation : func (offset int64 , count int64 , ctx context. Context ) error {
104104 // This function is called once per block.
105105 // It is passed this block's offset within the buffer and its count of bytes
106106 // Prepare to read the proper block/section of the buffer
@@ -198,12 +198,12 @@ func downloadBlobToBuffer(ctx context.Context, blobURL BlobURL, offset int64, co
198198 progress := int64 (0 )
199199 progressLock := & sync.Mutex {}
200200
201- err := doBatchTransfer (ctx , batchTransferOptions {
202- operationName : "downloadBlobToBuffer" ,
203- transferSize : count ,
204- chunkSize : o .BlockSize ,
205- parallelism : o .Parallelism ,
206- operation : func (chunkStart int64 , count int64 ) error {
201+ err := DoBatchTransfer (ctx , BatchTransferOptions {
202+ OperationName : "downloadBlobToBuffer" ,
203+ TransferSize : count ,
204+ ChunkSize : o .BlockSize ,
205+ Parallelism : o .Parallelism ,
206+ Operation : func (chunkStart int64 , count int64 , ctx context. Context ) error {
207207 dr , err := blobURL .Download (ctx , chunkStart + offset , count , o .AccessConditions , false )
208208 if err != nil {
209209 return err
@@ -285,64 +285,69 @@ func DownloadBlobToFile(ctx context.Context, blobURL BlobURL, offset int64, coun
285285
286286///////////////////////////////////////////////////////////////////////////////
287287
288- // BatchTransferOptions identifies options used by doBatchTransfer .
289- type batchTransferOptions struct {
290- transferSize int64
291- chunkSize int64
292- parallelism uint16
293- operation func (offset int64 , chunkSize int64 ) error
294- operationName string
288+ // BatchTransferOptions identifies options used by DoBatchTransfer .
289+ type BatchTransferOptions struct {
290+ TransferSize int64
291+ ChunkSize int64
292+ Parallelism uint16
293+ Operation func (offset int64 , chunkSize int64 , ctx context. Context ) error
294+ OperationName string
295295}
296296
297- // doBatchTransfer helps to execute operations in a batch manner.
298- func doBatchTransfer (ctx context.Context , o batchTransferOptions ) error {
297+ // DoBatchTransfer helps to execute operations in a batch manner.
298+ // Can be used by users to customize batch works (for other scenarios that the SDK does not provide)
299+ func DoBatchTransfer (ctx context.Context , o BatchTransferOptions ) error {
300+ if o .ChunkSize == 0 {
301+ return errors .New ("ChunkSize cannot be 0" )
302+ }
303+
299304 // Prepare and do parallel operations.
300- numChunks := uint16 (((o .transferSize - 1 ) / o .chunkSize ) + 1 )
301- operationChannel := make (chan func () error , o .parallelism ) // Create the channel that release 'parallelism ' goroutines concurrently
305+ numChunks := uint16 (((o .TransferSize - 1 ) / o .ChunkSize ) + 1 )
306+ operationChannel := make (chan func () error , o .Parallelism ) // Create the channel that release 'Parallelism ' goroutines concurrently
302307 operationResponseChannel := make (chan error , numChunks ) // Holds each response
303308 ctx , cancel := context .WithCancel (ctx )
304309 defer cancel ()
305310
306311 // Create the goroutines that process each operation (in parallel).
307- if o .parallelism == 0 {
308- o .parallelism = 5 // default parallelism
312+ if o .Parallelism == 0 {
313+ o .Parallelism = 5 // default Parallelism
309314 }
310- for g := uint16 (0 ); g < o .parallelism ; g ++ {
315+ for g := uint16 (0 ); g < o .Parallelism ; g ++ {
311316 //grIndex := g
312317 go func () {
313318 for f := range operationChannel {
314- //fmt.Printf("[%s] gr-%d start action\n", o.operationName, grIndex)
315319 err := f ()
316320 operationResponseChannel <- err
317- //fmt.Printf("[%s] gr-%d end action\n", o.operationName, grIndex)
318321 }
319322 }()
320323 }
321324
322325 // Add each chunk's operation to the channel.
323326 for chunkNum := uint16 (0 ); chunkNum < numChunks ; chunkNum ++ {
324- curChunkSize := o .chunkSize
327+ curChunkSize := o .ChunkSize
325328
326329 if chunkNum == numChunks - 1 { // Last chunk
327- curChunkSize = o .transferSize - (int64 (chunkNum ) * o .chunkSize ) // Remove size of all transferred chunks from total
330+ curChunkSize = o .TransferSize - (int64 (chunkNum ) * o .ChunkSize ) // Remove size of all transferred chunks from total
328331 }
329- offset := int64 (chunkNum ) * o .chunkSize
332+ offset := int64 (chunkNum ) * o .ChunkSize
330333
331334 operationChannel <- func () error {
332- return o .operation (offset , curChunkSize )
335+ return o .Operation (offset , curChunkSize , ctx )
333336 }
334337 }
335338 close (operationChannel )
336339
337340 // Wait for the operations to complete.
341+ var firstErr error = nil
338342 for chunkNum := uint16 (0 ); chunkNum < numChunks ; chunkNum ++ {
339343 responseError := <- operationResponseChannel
340- if responseError != nil {
341- cancel () // As soon as any operation fails, cancel all remaining operation calls
342- return responseError // No need to process anymore responses
344+ // record the first error (the original error which should cause the other chunks to fail with canceled context)
345+ if responseError != nil && firstErr == nil {
346+ cancel () // As soon as any operation fails, cancel all remaining operation calls
347+ firstErr = responseError
343348 }
344349 }
345- return nil
350+ return firstErr
346351}
347352
348353////////////////////////////////////////////////////////////////////////////////////////////////
0 commit comments