storj.io/uplink@v1.13.0/private/eestream/decode.go (about)

     1  // Copyright (C) 2019 Storj Labs, Inc.
     2  // See LICENSE for copying information.
     3  
     4  package eestream
     5  
     6  import (
     7  	"context"
     8  	"io"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/zeebo/errs"
    13  
    14  	"storj.io/common/encryption"
    15  	"storj.io/common/errs2"
    16  	"storj.io/common/ranger"
    17  	"storj.io/common/readcloser"
    18  )
    19  
    20  type decodedReader struct {
    21  	ctx               context.Context
    22  	cancel            context.CancelFunc
    23  	readers           map[int]io.ReadCloser
    24  	scheme            ErasureScheme
    25  	stripeReader      *StripeReader
    26  	outbuf, outbufmem []byte
    27  	err               error
    28  	currentStripe     int64
    29  	expectedStripes   int64
    30  	close             sync.Once
    31  	closeErr          error
    32  }
    33  
    34  // DecodeReaders2 takes a map of readers and an ErasureScheme returning a
    35  // combined Reader.
    36  //
    37  // rs is a map of erasure piece numbers to erasure piece streams.
    38  // expectedSize is the number of bytes expected to be returned by the Reader.
    39  // mbm is the maximum memory (in bytes) to be allocated for read buffers. If
    40  // set to 0, the minimum possible memory will be used.
    41  // if forceErrorDetection is set to true then k+1 pieces will be always
    42  // required for decoding, so corrupted pieces can be detected.
    43  func DecodeReaders2(ctx context.Context, cancel func(), rs map[int]io.ReadCloser, es ErasureScheme, expectedSize int64, mbm int, forceErrorDetection bool) io.ReadCloser {
    44  	defer mon.Task()(&ctx)(nil)
    45  	if expectedSize < 0 {
    46  		return readcloser.FatalReadCloser(Error.New("negative expected size"))
    47  	}
    48  	if expectedSize%int64(es.StripeSize()) != 0 {
    49  		return readcloser.FatalReadCloser(
    50  			Error.New("expected size (%d) not a factor decoded block size (%d)",
    51  				expectedSize, es.StripeSize()))
    52  	}
    53  	if err := checkMBM(mbm); err != nil {
    54  		return readcloser.FatalReadCloser(err)
    55  	}
    56  	expectedStripes := expectedSize / int64(es.StripeSize())
    57  	dr := &decodedReader{
    58  		readers:         rs,
    59  		scheme:          es,
    60  		outbufmem:       make([]byte, 0, 32*1024),
    61  		expectedStripes: expectedStripes,
    62  	}
    63  
    64  	dr.stripeReader = NewStripeReader(rs, es, int(expectedStripes), forceErrorDetection)
    65  
    66  	dr.ctx, dr.cancel = ctx, cancel
    67  	// Kick off a goroutine to watch for context cancelation.
    68  	go func() {
    69  		<-dr.ctx.Done()
    70  		_ = dr.Close()
    71  	}()
    72  	return dr
    73  }
    74  
    75  func (dr *decodedReader) Read(p []byte) (n int, err error) {
    76  	ctx := dr.ctx
    77  
    78  	if len(dr.outbuf) == 0 {
    79  		// if the output buffer is empty, let's fill it again
    80  		// if we've already had an error, fail
    81  		if dr.err != nil {
    82  			return 0, dr.err
    83  		}
    84  		// return EOF is the expected stripes were read
    85  		if dr.currentStripe >= dr.expectedStripes {
    86  			dr.err = io.EOF
    87  			return 0, dr.err
    88  		}
    89  		// read the input buffers of the next stripe - may also decode it
    90  		var newStripes int
    91  		dr.outbuf, newStripes, dr.err = dr.stripeReader.ReadStripes(ctx, dr.currentStripe, dr.outbufmem)
    92  		dr.currentStripe += int64(newStripes)
    93  		if dr.err != nil {
    94  			return 0, dr.err
    95  		}
    96  	}
    97  
    98  	n = copy(p, dr.outbuf)
    99  	dr.outbuf = dr.outbuf[n:]
   100  	return n, nil
   101  }
   102  
   103  func (dr *decodedReader) Close() (err error) {
   104  	ctx := dr.ctx
   105  	defer mon.Task()(&ctx)(&err)
   106  	errorThreshold := len(dr.readers) - dr.scheme.RequiredCount()
   107  	var closeGroup errs2.Group
   108  	// avoid double close of readers
   109  	dr.close.Do(func() {
   110  		for _, r := range dr.readers {
   111  			r := r
   112  			closeGroup.Go(func() error {
   113  				return errs2.IgnoreCanceled(r.Close())
   114  			})
   115  		}
   116  
   117  		// close the stripe reader
   118  		closeGroup.Go(dr.stripeReader.Close)
   119  
   120  		// We'll add a separate cancellation, just in case the Closing for some reason
   121  		// does not finish in time. However, we don't want to call `dr.cancel` them
   122  		// immediately, because this would not allow the connections to be pooled.
   123  		ctxDelay, ctxDelayCancel := context.WithTimeout(ctx, time.Millisecond)
   124  		defer ctxDelayCancel()
   125  		go func() {
   126  			<-ctxDelay.Done()
   127  			dr.cancel()
   128  		}()
   129  
   130  		allErrors := closeGroup.Wait()
   131  		errorThreshold -= len(allErrors)
   132  		dr.closeErr = errs.Combine(allErrors...)
   133  	})
   134  	// ensure readers are definitely closed.
   135  	dr.cancel()
   136  
   137  	// TODO this is workaround, we need reorganize to return multiple errors or divide into fatal, non fatal
   138  	if errorThreshold < 0 {
   139  		return dr.closeErr
   140  	}
   141  	if dr.closeErr != nil {
   142  		mon.Event("close failed")
   143  	}
   144  	return nil
   145  }
   146  
   147  type decodedRanger struct {
   148  	es                  ErasureScheme
   149  	rrs                 map[int]ranger.Ranger
   150  	inSize              int64
   151  	mbm                 int // max buffer memory
   152  	forceErrorDetection bool
   153  }
   154  
   155  // Decode takes a map of Rangers and an ErasureScheme and returns a combined
   156  // Ranger.
   157  //
   158  // rrs is a map of erasure piece numbers to erasure piece rangers.
   159  // mbm is the maximum memory (in bytes) to be allocated for read buffers. If
   160  // set to 0, the minimum possible memory will be used.
   161  // if forceErrorDetection is set to true then k+1 pieces will be always
   162  // required for decoding, so corrupted pieces can be detected.
   163  func Decode(rrs map[int]ranger.Ranger, es ErasureScheme, mbm int, forceErrorDetection bool) (ranger.Ranger, error) {
   164  	if err := checkMBM(mbm); err != nil {
   165  		return nil, err
   166  	}
   167  	if len(rrs) < es.RequiredCount() {
   168  		return nil, Error.New("not enough readers to reconstruct data!")
   169  	}
   170  	size := int64(-1)
   171  	for _, rr := range rrs {
   172  		if size == -1 {
   173  			size = rr.Size()
   174  		} else if size != rr.Size() {
   175  			return nil, Error.New("decode failure: range reader sizes don't all match")
   176  		}
   177  	}
   178  	if size == -1 {
   179  		return ranger.ByteRanger(nil), nil
   180  	}
   181  	if size%int64(es.ErasureShareSize()) != 0 {
   182  		return nil, Error.New("invalid erasure decoder and range reader combo. "+
   183  			"range reader size (%d) must be a multiple of erasure encoder block size (%d)",
   184  			size, es.ErasureShareSize())
   185  	}
   186  	return &decodedRanger{
   187  		es:                  es,
   188  		rrs:                 rrs,
   189  		inSize:              size,
   190  		mbm:                 mbm,
   191  		forceErrorDetection: forceErrorDetection,
   192  	}, nil
   193  }
   194  
   195  func (dr *decodedRanger) Size() int64 {
   196  	blocks := dr.inSize / int64(dr.es.ErasureShareSize())
   197  	return blocks * int64(dr.es.StripeSize())
   198  }
   199  
   200  func (dr *decodedRanger) Range(ctx context.Context, offset, length int64) (_ io.ReadCloser, err error) {
   201  	defer mon.Task()(&ctx)(&err)
   202  
   203  	ctx, cancel := context.WithCancel(ctx)
   204  	// offset and length might not be block-aligned. figure out which
   205  	// blocks contain this request
   206  	firstBlock, blockCount := encryption.CalcEncompassingBlocks(offset, length, dr.es.StripeSize())
   207  
   208  	// go ask for ranges for all those block boundaries
   209  	readers := make(map[int]io.ReadCloser, len(dr.rrs))
   210  	for i, rr := range dr.rrs {
   211  		r, err := rr.Range(ctx, firstBlock*int64(dr.es.ErasureShareSize()), blockCount*int64(dr.es.ErasureShareSize()))
   212  		if err != nil {
   213  			readers[i] = readcloser.FatalReadCloser(err)
   214  		} else {
   215  			readers[i] = r
   216  		}
   217  	}
   218  
   219  	// decode from all those ranges
   220  	r := DecodeReaders2(ctx, cancel, readers, dr.es, blockCount*int64(dr.es.StripeSize()), dr.mbm, dr.forceErrorDetection)
   221  	// offset might start a few bytes in, potentially discard the initial bytes
   222  	_, err = io.CopyN(io.Discard, r, offset-firstBlock*int64(dr.es.StripeSize()))
   223  	if err != nil {
   224  		return nil, Error.Wrap(err)
   225  	}
   226  	// length might not have included all of the blocks, limit what we return
   227  	return readcloser.LimitReadCloser(r, length), nil
   228  }
   229  
   230  func checkMBM(mbm int) error {
   231  	if mbm < 0 {
   232  		return Error.New("negative max buffer memory")
   233  	}
   234  	return nil
   235  }