storj.io/uplink@v1.13.0/private/stream/upload_part.go (about)

     1  // Copyright (C) 2021 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package stream
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"sync"
    10  
    11  	"github.com/zeebo/errs"
    12  	"golang.org/x/sync/errgroup"
    13  
    14  	"storj.io/common/storj"
    15  	"storj.io/uplink/private/storage/streams"
    16  )
    17  
    18  // PartUpload implements Writer and Closer for writing to part.
    19  type PartUpload struct {
    20  	ctx      context.Context
    21  	streams  *streams.Store
    22  	writer   *io.PipeWriter
    23  	errgroup errgroup.Group
    24  
    25  	// mu protects closed
    26  	mu     sync.Mutex
    27  	closed bool
    28  
    29  	// metaMu protects meta
    30  	metaMu sync.RWMutex
    31  	meta   *streams.Meta
    32  }
    33  
    34  // NewUploadPart creates new part upload.
    35  func NewUploadPart(ctx context.Context, bucket, key string, streamID storj.StreamID, partNumber uint32, eTagCh <-chan []byte, streamsStore *streams.Store) *PartUpload {
    36  	reader, writer := io.Pipe()
    37  
    38  	upload := PartUpload{
    39  		ctx:     ctx,
    40  		streams: streamsStore,
    41  		writer:  writer,
    42  	}
    43  
    44  	upload.errgroup.Go(func() error {
    45  		part, err := streamsStore.PutPart(ctx, bucket, key, streamID, partNumber, eTagCh, reader)
    46  		if err != nil {
    47  			err = Error.Wrap(err)
    48  			return errs.Combine(err, reader.CloseWithError(err))
    49  		}
    50  
    51  		upload.metaMu.Lock()
    52  		upload.meta = &streams.Meta{
    53  			Size:     part.Size,
    54  			Modified: part.Modified,
    55  		}
    56  		upload.metaMu.Unlock()
    57  
    58  		return nil
    59  	})
    60  
    61  	return &upload
    62  }
    63  
    64  // close transitions the upload to being closed and returns an error
    65  // if it is already closed.
    66  func (upload *PartUpload) close() error {
    67  	upload.mu.Lock()
    68  	defer upload.mu.Unlock()
    69  
    70  	if upload.closed {
    71  		return Error.New("already closed")
    72  	}
    73  
    74  	upload.closed = true
    75  	return nil
    76  }
    77  
    78  // isClosed returns true if the upload is already closed.
    79  func (upload *PartUpload) isClosed() bool {
    80  	upload.mu.Lock()
    81  	defer upload.mu.Unlock()
    82  
    83  	return upload.closed
    84  }
    85  
    86  // Write writes len(data) bytes from data to the underlying data stream.
    87  //
    88  // See io.Writer for more details.
    89  func (upload *PartUpload) Write(data []byte) (n int, err error) {
    90  	if upload.isClosed() {
    91  		return 0, Error.New("already closed")
    92  	}
    93  	return upload.writer.Write(data)
    94  }
    95  
    96  // Commit closes the stream and releases the underlying resources.
    97  func (upload *PartUpload) Commit() error {
    98  	if err := upload.close(); err != nil {
    99  		return err
   100  	}
   101  
   102  	// Wait for our launched goroutine to return.
   103  	return errs.Combine(
   104  		upload.writer.Close(),
   105  		upload.errgroup.Wait(),
   106  	)
   107  }
   108  
   109  // Abort closes the stream with an error so that it does not successfully commit and
   110  // releases the underlying resources.
   111  func (upload *PartUpload) Abort() error {
   112  	if err := upload.close(); err != nil {
   113  		return err
   114  	}
   115  
   116  	// Wait for our launched goroutine to return. We do not need any of the
   117  	// errors from the abort because they will just be things stating that
   118  	// it was aborted.
   119  	_ = upload.writer.CloseWithError(Error.New("aborted"))
   120  	_ = upload.errgroup.Wait()
   121  
   122  	return nil
   123  }
   124  
   125  // Meta returns the part metadata.
   126  //
   127  // Will return nil if the upload is still in progress.
   128  func (upload *PartUpload) Meta() *streams.Meta {
   129  	upload.metaMu.RLock()
   130  	defer upload.metaMu.RUnlock()
   131  
   132  	// we can safely return the pointer because it doesn't change after the
   133  	// upload finishes
   134  	return upload.meta
   135  }