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 }