| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	"crypto/rand" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | var erasureEncodeDecodeTests = []struct { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	dataBlocks, parityBlocks   int | 
					
						
							|  |  |  | 	missingData, missingParity int | 
					
						
							|  |  |  | 	reconstructParity          bool | 
					
						
							|  |  |  | 	shouldFail                 bool | 
					
						
							|  |  |  | }{ | 
					
						
							|  |  |  | 	{dataBlocks: 2, parityBlocks: 2, missingData: 0, missingParity: 0, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 3, parityBlocks: 3, missingData: 1, missingParity: 0, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 4, parityBlocks: 4, missingData: 2, missingParity: 0, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 5, parityBlocks: 5, missingData: 0, missingParity: 1, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 6, parityBlocks: 6, missingData: 0, missingParity: 2, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 7, parityBlocks: 7, missingData: 1, missingParity: 1, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 8, parityBlocks: 8, missingData: 3, missingParity: 2, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 2, parityBlocks: 2, missingData: 2, missingParity: 1, reconstructParity: true, shouldFail: true}, | 
					
						
							|  |  |  | 	{dataBlocks: 4, parityBlocks: 2, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: true}, | 
					
						
							|  |  |  | 	{dataBlocks: 8, parityBlocks: 4, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: false}, | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | func TestErasureEncodeDecode(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	data := make([]byte, 256) | 
					
						
							|  |  |  | 	if _, err := io.ReadFull(rand.Reader, data); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to read random data: %v", err) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 	for i, test := range erasureEncodeDecodeTests { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		buffer := make([]byte, len(data), 2*len(data)) | 
					
						
							|  |  |  | 		copy(buffer, data) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												[feat]: change erasure coding default block size from 10MiB to 1MiB (#11721)
major performance improvements in range GETs to avoid large
read amplification when ranges are tiny and random
```
-------------------
Operation: GET
Operations: 142014 -> 339421
Duration: 4m50s -> 4m56s
* Average: +139.41% (+1177.3 MiB/s) throughput, +139.11% (+658.4) obj/s
* Fastest: +125.24% (+1207.4 MiB/s) throughput, +132.32% (+612.9) obj/s
* 50% Median: +139.06% (+1175.7 MiB/s) throughput, +133.46% (+660.9) obj/s
* Slowest: +203.40% (+1267.9 MiB/s) throughput, +198.59% (+753.5) obj/s
```
TTFB from 10MiB BlockSize
```
* First Access TTFB: Avg: 81ms, Median: 61ms, Best: 20ms, Worst: 2.056s
```
TTFB from 1MiB BlockSize
```
* First Access TTFB: Avg: 22ms, Median: 21ms, Best: 8ms, Worst: 91ms
```
Full object reads however do see a slight change which won't be
noticeable in real world, so not doing any comparisons
TTFB still had improvements with full object reads with 1MiB
```
* First Access TTFB: Avg: 68ms, Median: 35ms, Best: 11ms, Worst: 1.16s
```
v/s
TTFB with 10MiB
```
* First Access TTFB: Avg: 388ms, Median: 98ms, Best: 20ms, Worst: 4.156s
```
This change should affect all new uploads, previous uploads should
continue to work with business as usual. But dramatic improvements can
be seen with these changes.
											
										 
											2021-03-07 06:09:34 +08:00
										 |  |  | 		erasure, err := NewErasure(context.Background(), test.dataBlocks, test.parityBlocks, blockSizeV2) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			t.Fatalf("Test %d: failed to create erasure: %v", i, err) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 		encoded, err := erasure.EncodeData(context.Background(), buffer) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: failed to encode data: %v", i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		for j := range encoded[:test.missingData] { | 
					
						
							|  |  |  | 			encoded[j] = nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := test.dataBlocks; j < test.dataBlocks+test.missingParity; j++ { | 
					
						
							|  |  |  | 			encoded[j] = nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if test.reconstructParity { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			err = erasure.DecodeDataAndParityBlocks(context.Background(), encoded) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			err = erasure.DecodeDataBlocks(encoded) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err == nil && test.shouldFail { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: test should fail but it passed", i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil && !test.shouldFail { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: test should pass but it failed: %v", i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		decoded := encoded | 
					
						
							|  |  |  | 		if !test.shouldFail { | 
					
						
							|  |  |  | 			if test.reconstructParity { | 
					
						
							|  |  |  | 				for j := range decoded { | 
					
						
							|  |  |  | 					if decoded[j] == nil { | 
					
						
							|  |  |  | 						t.Errorf("Test %d: failed to reconstruct shard %d", i, j) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				for j := range decoded[:test.dataBlocks] { | 
					
						
							|  |  |  | 					if decoded[j] == nil { | 
					
						
							|  |  |  | 						t.Errorf("Test %d: failed to reconstruct data shard %d", i, j) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 			decodedData := new(bytes.Buffer) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			if _, err = writeDataBlocks(context.Background(), decodedData, decoded, test.dataBlocks, 0, int64(len(data))); err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 				t.Errorf("Test %d: failed to write data blocks: %v", i, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !bytes.Equal(decodedData.Bytes(), data) { | 
					
						
							|  |  |  | 				t.Errorf("Test %d: Decoded data does not match original data: got: %v want: %v", i, decodedData.Bytes(), data) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | // Setup for erasureCreateFile and erasureReadFile tests.
 | 
					
						
							|  |  |  | type erasureTestSetup struct { | 
					
						
							|  |  |  | 	dataBlocks   int | 
					
						
							|  |  |  | 	parityBlocks int | 
					
						
							|  |  |  | 	blockSize    int64 | 
					
						
							|  |  |  | 	diskPaths    []string | 
					
						
							|  |  |  | 	disks        []StorageAPI | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | // Returns an initialized setup for erasure tests.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 03:37:26 +08:00
										 |  |  | func newErasureTestSetup(tb testing.TB, dataBlocks int, parityBlocks int, blockSize int64) (*erasureTestSetup, error) { | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 	diskPaths := make([]string, dataBlocks+parityBlocks) | 
					
						
							|  |  |  | 	disks := make([]StorageAPI, len(diskPaths)) | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:19 +08:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 	for i := range diskPaths { | 
					
						
							| 
									
										
										
										
											2022-07-26 03:37:26 +08:00
										 |  |  | 		disks[i], diskPaths[i], err = newXLStorageTestSetup(tb) | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-05 00:45:06 +08:00
										 |  |  | 		err = disks[i].MakeVol(context.Background(), "testbucket") | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 	return &erasureTestSetup{dataBlocks, parityBlocks, blockSize, diskPaths, disks}, nil | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | } |