storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/format-erasure_test.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2018 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"io/ioutil"
    23  	"os"
    24  	"reflect"
    25  	"testing"
    26  )
    27  
    28  // tests fixFormatErasureV3 - fix format.json on all disks.
    29  func TestFixFormatV3(t *testing.T) {
    30  	erasureDirs, err := getRandomDisks(8)
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  	for _, erasureDir := range erasureDirs {
    35  		defer os.RemoveAll(erasureDir)
    36  	}
    37  	endpoints := mustGetNewEndpoints(erasureDirs...)
    38  
    39  	storageDisks, errs := initStorageDisksWithErrors(endpoints)
    40  	for _, err := range errs {
    41  		if err != nil && err != errDiskNotFound {
    42  			t.Fatal(err)
    43  		}
    44  	}
    45  
    46  	format := newFormatErasureV3(1, 8)
    47  	format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
    48  	formats := make([]*formatErasureV3, 8)
    49  
    50  	for j := 0; j < 8; j++ {
    51  		newFormat := format.Clone()
    52  		newFormat.Erasure.This = format.Erasure.Sets[0][j]
    53  		formats[j] = newFormat
    54  	}
    55  
    56  	if err = initErasureMetaVolumesInLocalDisks(storageDisks, formats); err != nil {
    57  		t.Fatal(err)
    58  	}
    59  
    60  	formats[1] = nil
    61  	expThis := formats[2].Erasure.This
    62  	formats[2].Erasure.This = ""
    63  	if err := fixFormatErasureV3(storageDisks, endpoints, formats); err != nil {
    64  		t.Fatal(err)
    65  	}
    66  
    67  	newFormats, errs := loadFormatErasureAll(storageDisks, false)
    68  	for _, err := range errs {
    69  		if err != nil && err != errUnformattedDisk {
    70  			t.Fatal(err)
    71  		}
    72  	}
    73  	gotThis := newFormats[2].Erasure.This
    74  	if expThis != gotThis {
    75  		t.Fatalf("expected uuid %s, got %s", expThis, gotThis)
    76  	}
    77  }
    78  
    79  // tests formatErasureV3ThisEmpty conditions.
    80  func TestFormatErasureEmpty(t *testing.T) {
    81  	format := newFormatErasureV3(1, 16)
    82  	format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
    83  	formats := make([]*formatErasureV3, 16)
    84  
    85  	for j := 0; j < 16; j++ {
    86  		newFormat := format.Clone()
    87  		newFormat.Erasure.This = format.Erasure.Sets[0][j]
    88  		formats[j] = newFormat
    89  	}
    90  
    91  	// empty format to indicate disk not found, but this
    92  	// empty should return false.
    93  	formats[0] = nil
    94  
    95  	if ok := formatErasureV3ThisEmpty(formats); ok {
    96  		t.Fatalf("expected value false, got %t", ok)
    97  	}
    98  
    99  	formats[2].Erasure.This = ""
   100  	if ok := formatErasureV3ThisEmpty(formats); !ok {
   101  		t.Fatalf("expected value true, got %t", ok)
   102  	}
   103  }
   104  
   105  // Tests xl format migration.
   106  func TestFormatErasureMigrate(t *testing.T) {
   107  	// Get test root.
   108  	rootPath, err := getTestRoot()
   109  	if err != nil {
   110  		t.Fatal(err)
   111  	}
   112  	defer os.RemoveAll(rootPath)
   113  
   114  	m := &formatErasureV1{}
   115  	m.Format = formatBackendErasure
   116  	m.Version = formatMetaVersionV1
   117  	m.Erasure.Version = formatErasureVersionV1
   118  	m.Erasure.Disk = mustGetUUID()
   119  	m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()}
   120  
   121  	b, err := json.Marshal(m)
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  
   126  	if err = os.MkdirAll(pathJoin(rootPath, minioMetaBucket), os.FileMode(0755)); err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil {
   131  		t.Fatal(err)
   132  	}
   133  
   134  	if err = formatErasureMigrate(rootPath); err != nil {
   135  		t.Fatal(err)
   136  	}
   137  
   138  	migratedVersion, err := formatGetBackendErasureVersion(pathJoin(rootPath, minioMetaBucket, formatConfigFile))
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  
   143  	if migratedVersion != formatErasureVersionV3 {
   144  		t.Fatalf("expected version: %s, got: %s", formatErasureVersionV3, migratedVersion)
   145  	}
   146  
   147  	b, err = ioutil.ReadFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile))
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  	formatV3 := &formatErasureV3{}
   152  	if err = json.Unmarshal(b, formatV3); err != nil {
   153  		t.Fatal(err)
   154  	}
   155  	if formatV3.Erasure.This != m.Erasure.Disk {
   156  		t.Fatalf("expected disk uuid: %s, got: %s", m.Erasure.Disk, formatV3.Erasure.This)
   157  	}
   158  	if len(formatV3.Erasure.Sets) != 1 {
   159  		t.Fatalf("expected single set after migrating from v1 to v3, but found %d", len(formatV3.Erasure.Sets))
   160  	}
   161  	if !reflect.DeepEqual(formatV3.Erasure.Sets[0], m.Erasure.JBOD) {
   162  		t.Fatalf("expected disk uuid: %v, got: %v", m.Erasure.JBOD, formatV3.Erasure.Sets[0])
   163  	}
   164  
   165  	m = &formatErasureV1{}
   166  	m.Format = "unknown"
   167  	m.Version = formatMetaVersionV1
   168  	m.Erasure.Version = formatErasureVersionV1
   169  	m.Erasure.Disk = mustGetUUID()
   170  	m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()}
   171  
   172  	b, err = json.Marshal(m)
   173  	if err != nil {
   174  		t.Fatal(err)
   175  	}
   176  
   177  	if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	if err = formatErasureMigrate(rootPath); err == nil {
   182  		t.Fatal("Expected to fail with unexpected backend format")
   183  	}
   184  
   185  	m = &formatErasureV1{}
   186  	m.Format = formatBackendErasure
   187  	m.Version = formatMetaVersionV1
   188  	m.Erasure.Version = "30"
   189  	m.Erasure.Disk = mustGetUUID()
   190  	m.Erasure.JBOD = []string{m.Erasure.Disk, mustGetUUID(), mustGetUUID(), mustGetUUID()}
   191  
   192  	b, err = json.Marshal(m)
   193  	if err != nil {
   194  		t.Fatal(err)
   195  	}
   196  
   197  	if err = ioutil.WriteFile(pathJoin(rootPath, minioMetaBucket, formatConfigFile), b, os.FileMode(0644)); err != nil {
   198  		t.Fatal(err)
   199  	}
   200  
   201  	if err = formatErasureMigrate(rootPath); err == nil {
   202  		t.Fatal("Expected to fail with unexpected backend format version number")
   203  	}
   204  }
   205  
   206  // Tests check format xl value.
   207  func TestCheckFormatErasureValue(t *testing.T) {
   208  	testCases := []struct {
   209  		format  *formatErasureV3
   210  		success bool
   211  	}{
   212  		// Invalid Erasure format version "2".
   213  		{
   214  			&formatErasureV3{
   215  				formatMetaV1: formatMetaV1{
   216  					Version: "2",
   217  					Format:  "Erasure",
   218  				},
   219  				Erasure: struct {
   220  					Version          string     `json:"version"`
   221  					This             string     `json:"this"`
   222  					Sets             [][]string `json:"sets"`
   223  					DistributionAlgo string     `json:"distributionAlgo"`
   224  				}{
   225  					Version: "2",
   226  				},
   227  			},
   228  			false,
   229  		},
   230  		// Invalid Erasure format "Unknown".
   231  		{
   232  			&formatErasureV3{
   233  				formatMetaV1: formatMetaV1{
   234  					Version: "1",
   235  					Format:  "Unknown",
   236  				},
   237  				Erasure: struct {
   238  					Version          string     `json:"version"`
   239  					This             string     `json:"this"`
   240  					Sets             [][]string `json:"sets"`
   241  					DistributionAlgo string     `json:"distributionAlgo"`
   242  				}{
   243  					Version: "2",
   244  				},
   245  			},
   246  			false,
   247  		},
   248  		// Invalid Erasure format version "0".
   249  		{
   250  			&formatErasureV3{
   251  				formatMetaV1: formatMetaV1{
   252  					Version: "1",
   253  					Format:  "Erasure",
   254  				},
   255  				Erasure: struct {
   256  					Version          string     `json:"version"`
   257  					This             string     `json:"this"`
   258  					Sets             [][]string `json:"sets"`
   259  					DistributionAlgo string     `json:"distributionAlgo"`
   260  				}{
   261  					Version: "0",
   262  				},
   263  			},
   264  			false,
   265  		},
   266  	}
   267  
   268  	// Valid all test cases.
   269  	for i, testCase := range testCases {
   270  		if err := checkFormatErasureValue(testCase.format, nil); err != nil && testCase.success {
   271  			t.Errorf("Test %d: Expected failure %s", i+1, err)
   272  		}
   273  	}
   274  }
   275  
   276  // Tests getFormatErasureInQuorum()
   277  func TestGetFormatErasureInQuorumCheck(t *testing.T) {
   278  	setCount := 2
   279  	setDriveCount := 16
   280  
   281  	format := newFormatErasureV3(setCount, setDriveCount)
   282  	format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
   283  	formats := make([]*formatErasureV3, 32)
   284  
   285  	for i := 0; i < setCount; i++ {
   286  		for j := 0; j < setDriveCount; j++ {
   287  			newFormat := format.Clone()
   288  			newFormat.Erasure.This = format.Erasure.Sets[i][j]
   289  			formats[i*setDriveCount+j] = newFormat
   290  		}
   291  	}
   292  
   293  	// Return a format from list of formats in quorum.
   294  	quorumFormat, err := getFormatErasureInQuorum(formats)
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  
   299  	// Check if the reference format and input formats are same.
   300  	if err = formatErasureV3Check(quorumFormat, formats[0]); err != nil {
   301  		t.Fatal(err)
   302  	}
   303  
   304  	// QuorumFormat has .This field empty on purpose, expect a failure.
   305  	if err = formatErasureV3Check(formats[0], quorumFormat); err == nil {
   306  		t.Fatal("Unexpected success")
   307  	}
   308  
   309  	formats[0] = nil
   310  	quorumFormat, err = getFormatErasureInQuorum(formats)
   311  	if err != nil {
   312  		t.Fatal(err)
   313  	}
   314  
   315  	badFormat := *quorumFormat
   316  	badFormat.Erasure.Sets = nil
   317  	if err = formatErasureV3Check(quorumFormat, &badFormat); err == nil {
   318  		t.Fatal("Unexpected success")
   319  	}
   320  
   321  	badFormatUUID := *quorumFormat
   322  	badFormatUUID.Erasure.Sets[0][0] = "bad-uuid"
   323  	if err = formatErasureV3Check(quorumFormat, &badFormatUUID); err == nil {
   324  		t.Fatal("Unexpected success")
   325  	}
   326  
   327  	badFormatSetSize := *quorumFormat
   328  	badFormatSetSize.Erasure.Sets[0] = nil
   329  	if err = formatErasureV3Check(quorumFormat, &badFormatSetSize); err == nil {
   330  		t.Fatal("Unexpected success")
   331  	}
   332  
   333  	for i := range formats {
   334  		if i < 17 {
   335  			formats[i] = nil
   336  		}
   337  	}
   338  	if _, err = getFormatErasureInQuorum(formats); err == nil {
   339  		t.Fatal("Unexpected success")
   340  	}
   341  }
   342  
   343  // Tests formatErasureGetDeploymentID()
   344  func TestGetErasureID(t *testing.T) {
   345  	setCount := 2
   346  	setDriveCount := 8
   347  
   348  	format := newFormatErasureV3(setCount, setDriveCount)
   349  	format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
   350  	formats := make([]*formatErasureV3, 16)
   351  
   352  	for i := 0; i < setCount; i++ {
   353  		for j := 0; j < setDriveCount; j++ {
   354  			newFormat := format.Clone()
   355  			newFormat.Erasure.This = format.Erasure.Sets[i][j]
   356  			formats[i*setDriveCount+j] = newFormat
   357  		}
   358  	}
   359  
   360  	// Return a format from list of formats in quorum.
   361  	quorumFormat, err := getFormatErasureInQuorum(formats)
   362  	if err != nil {
   363  		t.Fatal(err)
   364  	}
   365  
   366  	// Check if the reference format and input formats are same.
   367  	var id string
   368  	if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil {
   369  		t.Fatal(err)
   370  	}
   371  
   372  	if id == "" {
   373  		t.Fatal("ID cannot be empty.")
   374  	}
   375  
   376  	formats[0] = nil
   377  	if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil {
   378  		t.Fatal(err)
   379  	}
   380  	if id == "" {
   381  		t.Fatal("ID cannot be empty.")
   382  	}
   383  
   384  	formats[1].Erasure.Sets[0][0] = "bad-uuid"
   385  	if id, err = formatErasureGetDeploymentID(quorumFormat, formats); err != nil {
   386  		t.Fatal(err)
   387  	}
   388  
   389  	if id == "" {
   390  		t.Fatal("ID cannot be empty.")
   391  	}
   392  
   393  	formats[2].ID = "bad-id"
   394  	if _, err = formatErasureGetDeploymentID(quorumFormat, formats); !errors.Is(err, errCorruptedFormat) {
   395  		t.Fatalf("Unexpect error %s", err)
   396  	}
   397  }
   398  
   399  // Initialize new format sets.
   400  func TestNewFormatSets(t *testing.T) {
   401  	setCount := 2
   402  	setDriveCount := 16
   403  
   404  	format := newFormatErasureV3(setCount, setDriveCount)
   405  	format.Erasure.DistributionAlgo = formatErasureVersionV2DistributionAlgoV1
   406  	formats := make([]*formatErasureV3, 32)
   407  	errs := make([]error, 32)
   408  
   409  	for i := 0; i < setCount; i++ {
   410  		for j := 0; j < setDriveCount; j++ {
   411  			newFormat := format.Clone()
   412  			newFormat.Erasure.This = format.Erasure.Sets[i][j]
   413  			formats[i*setDriveCount+j] = newFormat
   414  		}
   415  	}
   416  
   417  	quorumFormat, err := getFormatErasureInQuorum(formats)
   418  	if err != nil {
   419  		t.Fatal(err)
   420  	}
   421  
   422  	// 16th disk is unformatted.
   423  	errs[15] = errUnformattedDisk
   424  
   425  	newFormats := newHealFormatSets(quorumFormat, setCount, setDriveCount, formats, errs)
   426  	if newFormats == nil {
   427  		t.Fatal("Unexpected failure")
   428  	}
   429  
   430  	// Check if deployment IDs are preserved.
   431  	for i := range newFormats {
   432  		for j := range newFormats[i] {
   433  			if newFormats[i][j] == nil {
   434  				continue
   435  			}
   436  			if newFormats[i][j].ID != quorumFormat.ID {
   437  				t.Fatal("Deployment id in the new format is lost")
   438  			}
   439  		}
   440  	}
   441  }
   442  
   443  func BenchmarkInitStorageDisks256(b *testing.B) {
   444  	benchmarkInitStorageDisksN(b, 256)
   445  }
   446  
   447  func BenchmarkInitStorageDisks1024(b *testing.B) {
   448  	benchmarkInitStorageDisksN(b, 1024)
   449  }
   450  
   451  func BenchmarkInitStorageDisks2048(b *testing.B) {
   452  	benchmarkInitStorageDisksN(b, 2048)
   453  }
   454  
   455  func BenchmarkInitStorageDisksMax(b *testing.B) {
   456  	benchmarkInitStorageDisksN(b, 32*204)
   457  }
   458  
   459  func benchmarkInitStorageDisksN(b *testing.B, nDisks int) {
   460  	b.ResetTimer()
   461  	b.ReportAllocs()
   462  
   463  	fsDirs, err := getRandomDisks(nDisks)
   464  	if err != nil {
   465  		b.Fatal(err)
   466  	}
   467  	endpoints := mustGetNewEndpoints(fsDirs...)
   468  	b.RunParallel(func(pb *testing.PB) {
   469  		endpoints := endpoints
   470  		for pb.Next() {
   471  			initStorageDisksWithErrors(endpoints)
   472  		}
   473  	})
   474  }