github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/erasure-encode.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "context" 22 "fmt" 23 "io" 24 "sync" 25 ) 26 27 // Writes in parallel to writers 28 type parallelWriter struct { 29 writers []io.Writer 30 writeQuorum int 31 errs []error 32 } 33 34 // Write writes data to writers in parallel. 35 func (p *parallelWriter) Write(ctx context.Context, blocks [][]byte) error { 36 var wg sync.WaitGroup 37 38 for i := range p.writers { 39 if p.writers[i] == nil { 40 p.errs[i] = errDiskNotFound 41 continue 42 } 43 if p.errs[i] != nil { 44 continue 45 } 46 wg.Add(1) 47 go func(i int) { 48 defer wg.Done() 49 var n int 50 n, p.errs[i] = p.writers[i].Write(blocks[i]) 51 if p.errs[i] == nil { 52 if n != len(blocks[i]) { 53 p.errs[i] = io.ErrShortWrite 54 p.writers[i] = nil 55 } 56 } else { 57 p.writers[i] = nil 58 } 59 }(i) 60 } 61 wg.Wait() 62 63 // If nilCount >= p.writeQuorum, we return nil. This is because HealFile() uses 64 // CreateFile with p.writeQuorum=1 to accommodate healing of single disk. 65 // i.e if we do no return here in such a case, reduceWriteQuorumErrs() would 66 // return a quorum error to HealFile(). 67 nilCount := countErrs(p.errs, nil) 68 if nilCount >= p.writeQuorum { 69 return nil 70 } 71 72 writeErr := reduceWriteQuorumErrs(ctx, p.errs, objectOpIgnoredErrs, p.writeQuorum) 73 return fmt.Errorf("%w (offline-disks=%d/%d)", writeErr, countErrs(p.errs, errDiskNotFound), len(p.writers)) 74 } 75 76 // Encode reads from the reader, erasure-encodes the data and writes to the writers. 77 func (e *Erasure) Encode(ctx context.Context, src io.Reader, writers []io.Writer, buf []byte, quorum int) (total int64, err error) { 78 writer := ¶llelWriter{ 79 writers: writers, 80 writeQuorum: quorum, 81 errs: make([]error, len(writers)), 82 } 83 84 for { 85 var blocks [][]byte 86 n, err := io.ReadFull(src, buf) 87 if err != nil { 88 if !IsErrIgnored(err, []error{ 89 io.EOF, 90 io.ErrUnexpectedEOF, 91 }...) { 92 return 0, err 93 } 94 } 95 96 eof := err == io.EOF || err == io.ErrUnexpectedEOF 97 if n == 0 && total != 0 { 98 // Reached EOF, nothing more to be done. 99 break 100 } 101 102 // We take care of the situation where if n == 0 and total == 0 by creating empty data and parity files. 103 blocks, err = e.EncodeData(ctx, buf[:n]) 104 if err != nil { 105 return 0, err 106 } 107 108 if err = writer.Write(ctx, blocks); err != nil { 109 return 0, err 110 } 111 112 total += int64(n) 113 if eof { 114 break 115 } 116 } 117 return total, nil 118 }