diff --git a/cmd/erasure.go b/cmd/erasure.go index 288a7ee23..dd235b7de 100644 --- a/cmd/erasure.go +++ b/cmd/erasure.go @@ -18,6 +18,7 @@ package cmd import ( "context" + "sync" "github.com/klauspost/reedsolomon" "github.com/minio/minio/cmd/logger" @@ -25,7 +26,7 @@ import ( // Erasure - erasure encoding details. type Erasure struct { - encoder reedsolomon.Encoder + encoder func() reedsolomon.Encoder dataBlocks, parityBlocks int blockSize int64 } @@ -37,10 +38,29 @@ func NewErasure(ctx context.Context, dataBlocks, parityBlocks int, blockSize int parityBlocks: parityBlocks, blockSize: blockSize, } - e.encoder, err = reedsolomon.New(dataBlocks, parityBlocks, reedsolomon.WithAutoGoroutines(int(e.ShardSize()))) - if err != nil { - logger.LogIf(ctx, err) - return e, err + + // Check the parameters for sanity now. + if dataBlocks <= 0 || parityBlocks <= 0 { + return e, reedsolomon.ErrInvShardNum + } + + if dataBlocks+parityBlocks > 256 { + return e, reedsolomon.ErrMaxShardNum + } + + // Encoder when needed. + var enc reedsolomon.Encoder + var once sync.Once + e.encoder = func() reedsolomon.Encoder { + once.Do(func() { + e, err := reedsolomon.New(dataBlocks, parityBlocks, reedsolomon.WithAutoGoroutines(int(e.ShardSize()))) + if err != nil { + // Error conditions should be checked above. + panic(err) + } + enc = e + }) + return enc } return } @@ -51,12 +71,12 @@ func (e *Erasure) EncodeData(ctx context.Context, data []byte) ([][]byte, error) if len(data) == 0 { return make([][]byte, e.dataBlocks+e.parityBlocks), nil } - encoded, err := e.encoder.Split(data) + encoded, err := e.encoder().Split(data) if err != nil { logger.LogIf(ctx, err) return nil, err } - if err = e.encoder.Encode(encoded); err != nil { + if err = e.encoder().Encode(encoded); err != nil { logger.LogIf(ctx, err) return nil, err } @@ -77,13 +97,23 @@ func (e *Erasure) DecodeDataBlocks(data [][]byte) error { if !needsReconstruction { return nil } - return e.encoder.ReconstructData(data) + return e.encoder().ReconstructData(data) } // DecodeDataAndParityBlocks decodes the given erasure-coded data and verifies it. // It returns an error if the decoding failed. func (e *Erasure) DecodeDataAndParityBlocks(ctx context.Context, data [][]byte) error { - if err := e.encoder.Reconstruct(data); err != nil { + needsReconstruction := false + for _, b := range data { + if b == nil { + needsReconstruction = true + break + } + } + if !needsReconstruction { + return nil + } + if err := e.encoder().Reconstruct(data); err != nil { logger.LogIf(ctx, err) return err } diff --git a/cmd/xl-v1-healing-common.go b/cmd/xl-v1-healing-common.go index 3a2023d52..f7daef0bd 100644 --- a/cmd/xl-v1-healing-common.go +++ b/cmd/xl-v1-healing-common.go @@ -171,20 +171,15 @@ func disksWithAllParts(ctx context.Context, onlineDisks []StorageAPI, partsMetad switch scanMode { case madmin.HealDeepScan: - erasureInfo := partsMetadata[i].Erasure - erasure, err := NewErasure(ctx, erasureInfo.DataBlocks, erasureInfo.ParityBlocks, erasureInfo.BlockSize) - if err != nil { - dataErrs[i] = err - continue - } + erasure := partsMetadata[i].Erasure // disk has a valid xl.json but may not have all the // parts. This is considered an outdated disk, since // it needs healing too. for _, part := range partsMetadata[i].Parts { - checksumInfo := erasureInfo.GetChecksumInfo(part.Number) + checksumInfo := erasure.GetChecksumInfo(part.Number) partPath := pathJoin(object, fmt.Sprintf("part.%d", part.Number)) - err = onlineDisk.VerifyFile(bucket, partPath, erasure.ShardFileSize(part.Size), checksumInfo.Algorithm, checksumInfo.Hash, erasure.ShardSize()) + err := onlineDisk.VerifyFile(bucket, partPath, erasure.ShardFileSize(part.Size), checksumInfo.Algorithm, checksumInfo.Hash, erasure.ShardSize()) if err != nil { if !IsErr(err, []error{ errFileNotFound, diff --git a/cmd/xl-v1-metadata.go b/cmd/xl-v1-metadata.go index 32c03a110..1b49f8a6b 100644 --- a/cmd/xl-v1-metadata.go +++ b/cmd/xl-v1-metadata.go @@ -139,6 +139,25 @@ func (e ErasureInfo) GetChecksumInfo(partNumber int) (ckSum ChecksumInfo) { return ChecksumInfo{} } +// ShardFileSize - returns final erasure size from original size. +func (e ErasureInfo) ShardFileSize(totalLength int64) int64 { + if totalLength == 0 { + return 0 + } + if totalLength == -1 { + return -1 + } + numShards := totalLength / e.BlockSize + lastBlockSize := totalLength % e.BlockSize + lastShardSize := ceilFrac(lastBlockSize, int64(e.DataBlocks)) + return numShards*e.ShardSize() + lastShardSize +} + +// ShardSize - returns actual shared size from erasure blockSize. +func (e ErasureInfo) ShardSize() int64 { + return ceilFrac(e.BlockSize, int64(e.DataBlocks)) +} + // statInfo - carries stat information of the object. type statInfo struct { Size int64 `json:"size"` // Size of the object `xl.json`. diff --git a/go.mod b/go.mod index f25e9d024..672ccc2da 100644 --- a/go.mod +++ b/go.mod @@ -51,10 +51,10 @@ require ( github.com/jonboulle/clockwork v0.1.0 // indirect github.com/json-iterator/go v1.1.9 github.com/klauspost/compress v1.10.3 - github.com/klauspost/cpuid v1.2.2 + github.com/klauspost/cpuid v1.2.4 github.com/klauspost/pgzip v1.2.1 github.com/klauspost/readahead v1.3.1 - github.com/klauspost/reedsolomon v1.9.3 + github.com/klauspost/reedsolomon v1.9.7 github.com/lib/pq v1.1.1 github.com/mattn/go-colorable v0.1.1 github.com/mattn/go-ieproxy v0.0.0-20190805055040-f9202b1cfdeb // indirect diff --git a/go.sum b/go.sum index 938fcca82..829bfaedb 100644 --- a/go.sum +++ b/go.sum @@ -218,12 +218,16 @@ github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eT github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us= github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.4 h1:EBfaK0SWSwk+fgk6efYFWdzl8MwRWoOO1gkmiaTXPW4= +github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM= github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= github.com/klauspost/readahead v1.3.1 h1:QqXNYvm+VvqYcbrRT4LojUciM0XrznFRIDrbHiJtu/0= github.com/klauspost/readahead v1.3.1/go.mod h1:AH9juHzNH7xqdqFHrMRSHeH2Ps+vFf+kblDqzPFiLJg= github.com/klauspost/reedsolomon v1.9.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY= github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/klauspost/reedsolomon v1.9.7 h1:+azeqnT4iNG9UEcWC+7utJ4xXQ9S8pSlkZor0DOArEQ= +github.com/klauspost/reedsolomon v1.9.7/go.mod h1:+8WD025Xpby8/kG5h/HDPIFhiiuGEtZOKw+5Y4drAD8= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=