github.com/johnathanhowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/host/storagemanager/storagefolders_smoke_test.go (about)

     1  package storagemanager
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"io/ioutil"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/NebulousLabs/Sia/crypto"
    13  	"github.com/NebulousLabs/Sia/modules"
    14  	"github.com/NebulousLabs/Sia/types"
    15  
    16  	"github.com/NebulousLabs/bolt"
    17  )
    18  
    19  // sectorUsageCheck compares a manually maintained sector usage map to the
    20  // manager's internal sector usage map, and returns an error if there are any
    21  // inconsistencies.
    22  func (smt *storageManagerTester) sectorUsageCheck(sectorUsageMap map[crypto.Hash][]types.BlockHeight) error {
    23  	// Check that the in-database representation for the sector usage map
    24  	// matches the in-memory understanding of what the sector map should be
    25  	return smt.sm.db.View(func(tx *bolt.Tx) error {
    26  		bsu := tx.Bucket(bucketSectorUsage)
    27  		// Make sure that the number of sectors in the sector usage map and the
    28  		// number of sectors in the database are the same.
    29  		if len(sectorUsageMap) != bsu.Stats().KeyN {
    30  			return errors.New("BucketSectorUsage has the wrong number of sectors recorded")
    31  		}
    32  
    33  		// For every sector in the sector usage map, make sure the database has
    34  		// a matching sector with the right expiry information.
    35  		for sectorRoot, expiryHeights := range sectorUsageMap {
    36  			usageBytes := bsu.Get(smt.sm.sectorID(sectorRoot[:]))
    37  			if usageBytes == nil {
    38  				return errors.New("no usage info on known sector")
    39  			}
    40  			var usage sectorUsage
    41  			err := json.Unmarshal(usageBytes, &usage)
    42  			if err != nil {
    43  				return err
    44  			}
    45  			if len(usage.Expiry) != len(expiryHeights) {
    46  				return errors.New("usage information mismatch")
    47  			}
    48  			for i, expiryHeight := range usage.Expiry {
    49  				if expiryHeight != expiryHeights[i] {
    50  					// The correctness could be made not-implementation
    51  					// dependent by sorting the two arrays before comparing
    52  					// them, but that was deemed an unneeded step for this
    53  					// test.
    54  					return errors.New("usage expiry height mismatch - correctness is implementation dependent")
    55  				}
    56  			}
    57  		}
    58  		return nil
    59  	})
    60  }
    61  
    62  // TestStorageFolderUsage is a general integration test which tries all of the
    63  // major storage folder operations in various orders, all while adding and
    64  // removing sectors to verify that the behavior works as expected.
    65  func TestStorageFolderUsage(t *testing.T) {
    66  	if testing.Short() {
    67  		t.SkipNow()
    68  	}
    69  	t.Parallel()
    70  	smt, err := newStorageManagerTester("TestStorageFolderUsage")
    71  	if err != nil {
    72  		t.Fatal(err)
    73  	}
    74  
    75  	// Start by checking that the initial state of the manager has no storage
    76  	// added to it.
    77  	totalStorage, remainingStorage := smt.sm.capacity()
    78  	if totalStorage != 0 || remainingStorage != 0 {
    79  		t.Error("initial capacity of manager is not reported at 0 - but no drives have been added!")
    80  	}
    81  
    82  	// Try adding a sector when there are no storage folders.
    83  	sectorRoot, sectorData, err := createSector()
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	err = smt.sm.AddSector(sectorRoot, 10, sectorData)
    88  	if err != errInsufficientStorageForSector {
    89  		t.Fatal(err)
    90  	}
    91  
    92  	// Add a storage folder, simulating a new drive being connected to the
    93  	// manager.
    94  	storageFolderOne := filepath.Join(smt.persistDir, "manager drive 1")
    95  	// Try using a file size that is too small. Because a filesize check is
    96  	// quicker than a disk check, the filesize check should come first.
    97  	err = smt.sm.AddStorageFolder(storageFolderOne, minimumStorageFolderSize-1)
    98  	if err != errSmallStorageFolder {
    99  		t.Fatal("expecting errSmallStorageFolder:", err)
   100  	}
   101  	// Try a file size that is too large.
   102  	err = smt.sm.AddStorageFolder(storageFolderOne, maximumStorageFolderSize+1)
   103  	if err != errLargeStorageFolder {
   104  		t.Fatal("expecting errLargeStorageFolder:", err)
   105  	}
   106  	// Try linking to a storage folder that does not exist.
   107  	err = smt.sm.AddStorageFolder(storageFolderOne, minimumStorageFolderSize)
   108  	if err == nil {
   109  		t.Fatal("should not be able to link to a storage folder which does not exist")
   110  	}
   111  	// Try linking to a storage folder that's not a directory.
   112  	err = ioutil.WriteFile(storageFolderOne, make([]byte, minimumStorageFolderSize), 0700)
   113  	if err != nil {
   114  		t.Fatal(err)
   115  	}
   116  	err = smt.sm.AddStorageFolder(storageFolderOne, minimumStorageFolderSize)
   117  	if err != errStorageFolderNotFolder {
   118  		t.Fatal(err)
   119  	}
   120  	// Try linking to a storage folder that is a directory.
   121  	err = os.Remove(storageFolderOne)
   122  	if err != nil {
   123  		t.Fatal(err)
   124  	}
   125  	err = os.Mkdir(storageFolderOne, 0700)
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  	err = smt.sm.AddStorageFolder(storageFolderOne, minimumStorageFolderSize)
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	// Do a probabilistic reset of the manager, to verify that the persistence
   134  	// structures can reboot without causing issues.
   135  	err = smt.probabilisticReset()
   136  	if err != nil {
   137  		t.Fatal(err)
   138  	}
   139  	// Check that the manager has correctly updated the amount of total storage.
   140  	totalStorage, remainingStorage = smt.sm.capacity()
   141  	if totalStorage != minimumStorageFolderSize || remainingStorage != minimumStorageFolderSize {
   142  		t.Error("manager capacity has not been correctly updated after adding a storage folder")
   143  		t.Error(totalStorage, minimumStorageFolderSize, remainingStorage)
   144  	}
   145  
   146  	// Add a second storage folder.
   147  	storageFolderTwo := filepath.Join(smt.persistDir, "managerDrive2")
   148  	err = os.Mkdir(storageFolderTwo, 0700)
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  	err = smt.sm.AddStorageFolder(storageFolderTwo, minimumStorageFolderSize*2)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	// Do a probabilistic reset of the manager, to verify that the persistence
   157  	// structures can reboot without causing issues.
   158  	err = smt.probabilisticReset()
   159  	if err != nil {
   160  		t.Fatal(err)
   161  	}
   162  	// Check that the manager has correctly updated the amount of total
   163  	// storage.
   164  	totalStorage, remainingStorage = smt.sm.capacity()
   165  	if totalStorage != minimumStorageFolderSize*3 || remainingStorage != minimumStorageFolderSize*3 {
   166  		t.Error("manager capacity has not been correctly updated after adding a storage folder")
   167  	}
   168  	// Try removing the storage folder using illegal values.
   169  	err = smt.sm.RemoveStorageFolder(-1, false)
   170  	if err != errBadStorageFolderIndex {
   171  		t.Fatal(err)
   172  	}
   173  	err = smt.sm.RemoveStorageFolder(2, false)
   174  	if err != errBadStorageFolderIndex {
   175  		t.Fatal(err)
   176  	}
   177  
   178  	// Try removing the second storage folder. Before removing the storage
   179  	// folder, grab the path of the symlink so we can check later that it was
   180  	// properly removed from the filesystem.
   181  	symPath := filepath.Join(smt.sm.persistDir, smt.sm.storageFolders[1].uidString())
   182  	// Remove the storage folder.
   183  	err = smt.sm.RemoveStorageFolder(1, false)
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	// Do a probabilistic reset of the manager, to verify that the persistence
   188  	// structures can reboot without causing issues.
   189  	err = smt.probabilisticReset()
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	// Check that the manager has correctly updated the amount of total
   194  	// storage.
   195  	totalStorage, remainingStorage = smt.sm.capacity()
   196  	if totalStorage != minimumStorageFolderSize || remainingStorage != minimumStorageFolderSize {
   197  		t.Error("manager capacity has not been correctly updated after adding a storage folder")
   198  	}
   199  	_, err = os.Stat(symPath)
   200  	if err == nil || !os.IsNotExist(err) {
   201  		t.Error("Does not appear that the sympath was removed from disk:", err)
   202  	}
   203  
   204  	// No sectors added yet, the storage folder statistics should all be clean.
   205  	for _, sf := range smt.sm.storageFolders {
   206  		if sf.SuccessfulReads != 0 || sf.SuccessfulWrites != 0 || sf.FailedReads != 0 || sf.FailedWrites != 0 {
   207  			t.Error("storage folder does not have blank health stats")
   208  		}
   209  	}
   210  
   211  	// Retry adding the sector, the add should succeed and the amount of
   212  	// remaining storage should be updated.
   213  	sectorExpiry := types.BlockHeight(10)
   214  	err = smt.sm.AddSector(sectorRoot, sectorExpiry, sectorData)
   215  	if err != nil {
   216  		t.Fatal(err)
   217  	}
   218  	// Check that the capacity has updated to reflected the new sector.
   219  	totalStorage, remainingStorage = smt.sm.capacity()
   220  	if totalStorage != minimumStorageFolderSize || remainingStorage != minimumStorageFolderSize-modules.SectorSize {
   221  		t.Error("manager capacity has not been correctly updated after adding a sector", totalStorage, remainingStorage)
   222  	}
   223  	// Check that the sector has been added to the filesystem correctly - the
   224  	// file should exist in storageFolderOne, and the data in the file should
   225  	// match the data of the sector.
   226  	sectorPath := filepath.Join(storageFolderOne, string(smt.sm.sectorID(sectorRoot[:])))
   227  	err = func() error {
   228  		sectorFile, err := os.Open(sectorPath)
   229  		defer sectorFile.Close()
   230  		fileInfo, err := sectorFile.Stat()
   231  		if err != nil {
   232  			return err
   233  		}
   234  		if uint64(fileInfo.Size()) != modules.SectorSize {
   235  			return errors.New("scanned sector is not the right size")
   236  		}
   237  		readSectorData, err := ioutil.ReadAll(sectorFile)
   238  		if err != nil {
   239  			return err
   240  		}
   241  		if bytes.Compare(readSectorData, sectorData) != 0 {
   242  			return errors.New("read sector does not match sector data")
   243  		}
   244  		return nil
   245  	}()
   246  	if err != nil {
   247  		t.Fatal(err)
   248  	}
   249  	// Check that the sector as represented in the database has the correct
   250  	// height values.
   251  	err = smt.sm.db.View(func(tx *bolt.Tx) error {
   252  		bsu := tx.Bucket(bucketSectorUsage)
   253  		usageBytes := bsu.Get(smt.sm.sectorID(sectorRoot[:]))
   254  		var usage sectorUsage
   255  		err := json.Unmarshal(usageBytes, &usage)
   256  		if err != nil {
   257  			return err
   258  		}
   259  		if len(usage.Expiry) != 1 {
   260  			return errors.New("wrong usage expiry length in BucketSectorUsage")
   261  		}
   262  		if usage.Expiry[0] != 10 {
   263  			return errors.New("usage expiry for sector is set to the wrong height")
   264  		}
   265  		return nil
   266  	})
   267  	if err != nil {
   268  		t.Fatal(err)
   269  	}
   270  	// Check that the disk health stats match the expected values.
   271  	for _, sf := range smt.sm.storageFolders {
   272  		if sf.SuccessfulReads != 0 || sf.SuccessfulWrites != 1 || sf.FailedReads != 0 || sf.FailedWrites != 0 {
   273  			t.Error("storage folder does not have blank health stats")
   274  		}
   275  	}
   276  
   277  	// Try to resize the storage folder. While resizing the storage folder, try
   278  	// a bunch of invalid resize calls.
   279  	err = smt.sm.ResizeStorageFolder(1, minimumStorageFolderSize-1)
   280  	if err != errBadStorageFolderIndex {
   281  		t.Error(err)
   282  	}
   283  	err = smt.sm.ResizeStorageFolder(-1, minimumStorageFolderSize-1)
   284  	if err != errBadStorageFolderIndex {
   285  		t.Error(err)
   286  	}
   287  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize-1)
   288  	if err != errSmallStorageFolder {
   289  		t.Error(err)
   290  	}
   291  	err = smt.sm.ResizeStorageFolder(0, maximumStorageFolderSize+1)
   292  	if err != errLargeStorageFolder {
   293  		t.Error(err)
   294  	}
   295  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize*10)
   296  	if err != nil {
   297  		t.Fatal(err)
   298  	}
   299  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize*10)
   300  	if err != errNoResize {
   301  		t.Fatal(err)
   302  	}
   303  	// Do a probabilistic reset of the manager, to verify that the persistence
   304  	// structures can reboot without causing issues.
   305  	err = smt.probabilisticReset()
   306  	if err != nil {
   307  		t.Fatal(err)
   308  	}
   309  	// Manager should be able to support having uneven storage sizes.
   310  	oddStorageSize := (minimumStorageFolderSize) + modules.SectorSize*3 + 3
   311  	err = smt.sm.ResizeStorageFolder(0, oddStorageSize)
   312  	if err != nil {
   313  		t.Fatal(err)
   314  	}
   315  
   316  	// Create a sector list, containing all sectors (including repeats) and the
   317  	// heights at which they expire. This sector list will be updated as
   318  	// sectors are added and removed.
   319  	sectorUsageMap := make(map[crypto.Hash][]types.BlockHeight)
   320  	sectorUsageMap[sectorRoot] = []types.BlockHeight{sectorExpiry}
   321  	// Sanity check - manager should not have any sectors in it.
   322  	totalStorage, remainingStorage = smt.sm.capacity()
   323  	if totalStorage != remainingStorage+modules.SectorSize {
   324  		t.Fatal("manager is not empty at the moment of creating the in-memory sector usage map")
   325  	}
   326  	// Verify that the initial sector usage map was created correctly.
   327  	err = smt.sectorUsageCheck(sectorUsageMap)
   328  	if err != nil {
   329  		t.Fatal(err)
   330  	}
   331  
   332  	// Fill the storage folder above the minimum size, then try to shrink it to
   333  	// the minimum size.
   334  	for i := uint64(0); i <= minimumStorageFolderSize/modules.SectorSize; i++ {
   335  		sectorRoot, sectorData, err := createSector()
   336  		if err != nil {
   337  			t.Fatal(err)
   338  		}
   339  		err = smt.sm.AddSector(sectorRoot, 86+types.BlockHeight(i), sectorData)
   340  		if err != nil {
   341  			t.Fatal(err)
   342  		}
   343  		// Do a probabilistic reset of the manager, to verify that the persistence
   344  		// structures can reboot without causing issues.
   345  		err = smt.probabilisticReset()
   346  		if err != nil {
   347  			t.Fatal(err)
   348  		}
   349  		// Now that there is a sector usage map, it must be kept consistent
   350  		// with the sector usage in the manager.
   351  		sectorUsageMap[sectorRoot] = []types.BlockHeight{86 + types.BlockHeight(i)}
   352  	}
   353  	oldSize := smt.sm.storageFolders[0].Size
   354  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize)
   355  	if err != errIncompleteOffload {
   356  		t.Fatal(err)
   357  	}
   358  	size := smt.sm.storageFolders[0].Size
   359  	sizeRemaining := smt.sm.storageFolders[0].SizeRemaining
   360  	if size >= oldSize || sizeRemaining > 0 {
   361  		t.Fatal("manager did not correctly update the size remaining after an incomplete shrink")
   362  	}
   363  
   364  	// Try adding another sector, there should not be enough room.
   365  	sr, sd, err := createSector()
   366  	if err != nil {
   367  		t.Fatal(err)
   368  	}
   369  	err = smt.sm.AddSector(sr, 186, sd)
   370  	if err != errInsufficientStorageForSector {
   371  		t.Fatal(err)
   372  	}
   373  
   374  	// Add a second folder, and add a sector to that folder. There should be
   375  	// enough space remaining in the first folder for the removal to be
   376  	// successful.
   377  	err = smt.sm.AddStorageFolder(storageFolderTwo, minimumStorageFolderSize*2)
   378  	if err != nil {
   379  		t.Fatal(err)
   380  	}
   381  	// Do a probabilistic reset of the manager, to verify that the persistence
   382  	// structures can reboot without causing issues.
   383  	err = smt.probabilisticReset()
   384  	if err != nil {
   385  		t.Fatal(err)
   386  	}
   387  	sectorRoot, sectorData, err = createSector()
   388  	if err != nil {
   389  		t.Fatal(err)
   390  	}
   391  	err = smt.sm.AddSector(sectorRoot, 81, sectorData)
   392  	if err != nil {
   393  		t.Fatal(err)
   394  	}
   395  	sectorUsageMap[sectorRoot] = []types.BlockHeight{81}
   396  	// Check that the sector ended up in the right storage folder - because the
   397  	// second storage folder is the least full, the sector should end up there.
   398  	folderTwoUsage := smt.sm.storageFolders[1].Size - smt.sm.storageFolders[1].SizeRemaining
   399  	if folderTwoUsage != modules.SectorSize {
   400  		t.Error("sector did not appear to land in the right storage folder")
   401  	}
   402  	// Check the filesystem. The folder for storage folder 1 should have 10
   403  	// files, and the folder for storage folder 2 should have 1 file.
   404  	infos, err := ioutil.ReadDir(storageFolderOne)
   405  	if err != nil {
   406  		t.Fatal(err)
   407  	}
   408  	if len(infos) != 10 {
   409  		t.Fatal("storage folder one should have 10 sectors in it")
   410  	}
   411  	infos, err = ioutil.ReadDir(storageFolderTwo)
   412  	if err != nil {
   413  		t.Fatal(err)
   414  	}
   415  	if len(infos) != 1 {
   416  		t.Fatal("storage folder two should have 1 sector in it")
   417  	}
   418  
   419  	// The first storage folder has more sectors than the minimum allowed
   420  	// amount. Reduce the size of the first storage folder to minimum, which
   421  	// should be accepted but will result in sectors being transferred to the
   422  	// second storage folder.
   423  	totalStorage, remainingStorage = smt.sm.capacity()
   424  	prevStorage := totalStorage
   425  	usedStorage := totalStorage - remainingStorage
   426  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize)
   427  	if err != nil {
   428  		t.Fatal(err)
   429  	}
   430  	totalStorage, remainingStorage = smt.sm.capacity()
   431  	if usedStorage != totalStorage-remainingStorage {
   432  		t.Error("the used storage value adjusted after removing a storage folder", usedStorage, totalStorage-remainingStorage)
   433  	}
   434  	if totalStorage >= prevStorage {
   435  		t.Error("total storage was not adjusted correctly after removing a storage folder")
   436  	}
   437  	// Check the filesystem.
   438  	infos, err = ioutil.ReadDir(storageFolderOne)
   439  	if err != nil {
   440  		t.Fatal(err)
   441  	}
   442  	if len(infos) != 8 {
   443  		t.Fatal("wrong number of sectors in storage folder one")
   444  	}
   445  	infos, err = ioutil.ReadDir(storageFolderTwo)
   446  	if len(infos) != 3 {
   447  		t.Fatal("wrong number of sectors in storage folder two")
   448  	}
   449  
   450  	// Remove the first storage folder, which should result in all of the
   451  	// sectors being moved to the second storage folder. Note that
   452  	// storageFolderTwo now has an index of '0'.
   453  	totalStorage, remainingStorage = smt.sm.capacity()
   454  	prevStorage = totalStorage
   455  	usedStorage = totalStorage - remainingStorage
   456  	symPath = filepath.Join(smt.sm.persistDir, smt.sm.storageFolders[0].uidString())
   457  	err = smt.sm.RemoveStorageFolder(0, false)
   458  	if err != nil {
   459  		t.Fatal(err)
   460  	}
   461  	totalStorage, remainingStorage = smt.sm.capacity()
   462  	if usedStorage != totalStorage-remainingStorage {
   463  		t.Error("the used storage value adjusted after removing a storage folder", usedStorage, totalStorage-remainingStorage)
   464  	}
   465  	if totalStorage == prevStorage {
   466  		t.Error("total storage was not adjusted after removing a storage folder")
   467  	}
   468  	// Check that the filesystem seems correct.
   469  	infos, err = ioutil.ReadDir(storageFolderTwo)
   470  	if err != nil {
   471  		t.Fatal(err)
   472  	}
   473  	if len(infos) != 11 {
   474  		t.Fatal("wrong number of sectors in folder")
   475  	}
   476  	_, err = os.Stat(symPath)
   477  	if !os.IsNotExist(err) {
   478  		t.Fatal("the sym link to the deleted storage folder should no longer exist")
   479  	}
   480  
   481  	// Add the first storage folder, resize the second storage folder back down
   482  	// to minimum. Note that storageFolderOne now has an index of '1', and
   483  	// storageFolderTwo now has an index of '0'.
   484  	err = smt.sm.AddStorageFolder(storageFolderOne, minimumStorageFolderSize)
   485  	if err != nil {
   486  		t.Fatal(err)
   487  	}
   488  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize)
   489  	if err != nil {
   490  		t.Fatal(err)
   491  	}
   492  	// Do a probabilistic reset of the manager, to verify that the persistence
   493  	// structures can reboot without causing issues.
   494  	err = smt.probabilisticReset()
   495  	if err != nil {
   496  		t.Fatal(err)
   497  	}
   498  	// Check the filesystem.
   499  	infos, err = ioutil.ReadDir(storageFolderTwo)
   500  	if err != nil {
   501  		t.Fatal(err)
   502  	}
   503  	if len(infos) != 8 {
   504  		t.Fatal("wrong number of sectors")
   505  	}
   506  	infos, err = ioutil.ReadDir(storageFolderOne)
   507  	if err != nil {
   508  		t.Fatal(err)
   509  	}
   510  	if len(infos) != 3 {
   511  		t.Fatal("wrong number of sectors")
   512  	}
   513  
   514  	// Add a bunch of sectors and repeat sectors at multiple colliding heights.
   515  	// Start by resizing the first storage folder so that there is enough room
   516  	// for the new sectors.
   517  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize*3)
   518  	if err != nil {
   519  		t.Fatal(err)
   520  	}
   521  	for i := types.BlockHeight(0); i < 10; i++ {
   522  		// Add 10 unique sectors to the map.
   523  		sectorRoot, sectorData, err := createSector()
   524  		if err != nil {
   525  			t.Fatal(err)
   526  		}
   527  		for j := types.BlockHeight(0); j < 5; j++ {
   528  			// Add the unique sectors at multiple heights, creating virtual
   529  			// sectors.
   530  			for k := types.BlockHeight(0); k < 4; k++ {
   531  				// Add in an extra loop so that height collisions can be
   532  				// created such that the collisions happen out of order.
   533  				// Sectors are added at height 10+j+k, which means that there
   534  				// will be many collisions for each height, but the collisions
   535  				// are not happening in sorted order. The manager is not
   536  				// expected to do sorting, but should also not be confused by a
   537  				// random order.
   538  				err = smt.sm.AddSector(sectorRoot, 10+j+k, sectorData)
   539  				if err != nil {
   540  					t.Fatal(err)
   541  				}
   542  
   543  				// Add the sector to the sectorUsageMap, so it can be deleted
   544  				// later.
   545  				expiryList, exists := sectorUsageMap[sectorRoot]
   546  				if exists {
   547  					sectorUsageMap[sectorRoot] = append(expiryList, 10+j+k)
   548  				} else {
   549  					sectorUsageMap[sectorRoot] = []types.BlockHeight{10 + j + k}
   550  				}
   551  			}
   552  			// Do a probabilistic reset of the manager, to verify that the
   553  			// persistence structures can reboot without causing issues.
   554  			err = smt.probabilisticReset()
   555  			if err != nil {
   556  				t.Fatal(err)
   557  			}
   558  		}
   559  	}
   560  	// Check that the amount of storage in use represents 10 sectors, and not
   561  	// more - all the others are repeats and shouldn't be counted.
   562  	totalStorage, remainingStorage = smt.sm.capacity()
   563  	if totalStorage != minimumStorageFolderSize*4 || remainingStorage != minimumStorageFolderSize*4-modules.SectorSize*21 {
   564  		t.Fatal("Manager not reporting expected storage capacity:", totalStorage, remainingStorage, minimumStorageFolderSize*4, minimumStorageFolderSize*4-modules.SectorSize*21)
   565  	}
   566  	// Check that the internal sector usage database of the manager has been
   567  	// updated correctly.
   568  	err = smt.sectorUsageCheck(sectorUsageMap)
   569  	if err != nil {
   570  		t.Fatal(err)
   571  	}
   572  	// Check the filesystem.
   573  	infos, err = ioutil.ReadDir(storageFolderTwo)
   574  	if err != nil {
   575  		t.Fatal(err)
   576  	}
   577  	if len(infos) != 16 {
   578  		t.Fatal("there should be 16 sectors in storage folder two")
   579  	}
   580  	infos, err = ioutil.ReadDir(storageFolderOne)
   581  	if err != nil {
   582  		t.Fatal(err)
   583  	}
   584  	if len(infos) != 5 {
   585  		t.Fatal("there should be 5 sectors in storage folder one")
   586  	}
   587  	// Try removing a non-repeat sector.
   588  	expiryHeights, exists := sectorUsageMap[sectorRoot]
   589  	if !exists || len(expiryHeights) != 1 {
   590  		t.Fatal("sector map doesn't match testing assumptions")
   591  	}
   592  	// Try some illegal sector removal operations before trying a legal one.
   593  	err = smt.sm.RemoveSector(sectorRoot, sectorExpiry+50e6)
   594  	if err != errSectorNotFound {
   595  		t.Fatal("wrong error when removing illegal sector:", err)
   596  	}
   597  	alteredRoot := sectorRoot
   598  	alteredRoot[0]++
   599  	err = smt.sm.RemoveSector(alteredRoot, 81)
   600  	if err != errSectorNotFound {
   601  		t.Fatal("wrong error when removing illegal sector:", err)
   602  	}
   603  	// Now try the legal sector removal.
   604  	sectorPath = filepath.Join(storageFolderOne, string(smt.sm.sectorID(sectorRoot[:])))
   605  	err = smt.sm.RemoveSector(sectorRoot, 81)
   606  	if err != nil {
   607  		t.Fatal(err)
   608  	}
   609  	// Do a probabilistic reset of the manager, to verify that the persistence
   610  	// structures can reboot without causing issues.
   611  	err = smt.probabilisticReset()
   612  	if err != nil {
   613  		t.Fatal(err)
   614  	}
   615  	// Update the sector usage map to reflect the departure of a sector.
   616  	delete(sectorUsageMap, sectorRoot)
   617  	// Check that the new capacity is being reported correctly.
   618  	totalStorage, remainingStorage = smt.sm.capacity()
   619  	if totalStorage != minimumStorageFolderSize*4 || remainingStorage != minimumStorageFolderSize*4-modules.SectorSize*20 {
   620  		t.Fatal("Manager not reporting expected storage capacity:")
   621  	}
   622  	// Run a sector usage check to make sure the manager is properly handling
   623  	// the usage information when deleting a sector.
   624  	err = smt.sectorUsageCheck(sectorUsageMap)
   625  	if err != nil {
   626  		t.Fatal(err)
   627  	}
   628  	// Check that the sector on-disk has been deleted.
   629  	_, err = os.Stat(sectorPath)
   630  	if !os.IsNotExist(err) {
   631  		t.Fatal(err)
   632  	}
   633  	// Check that the total number of sectors seen on disk is 20.
   634  	infos, err = ioutil.ReadDir(storageFolderOne)
   635  	if err != nil {
   636  		t.Fatal(err)
   637  	}
   638  	infos2, err := ioutil.ReadDir(storageFolderTwo)
   639  	if err != nil {
   640  		t.Fatal(err)
   641  	}
   642  	if len(infos)+len(infos2) != 20 {
   643  		t.Fatal("there should be 20 sectors total on disk at this point")
   644  	}
   645  
   646  	// Remove two of the duplicated sectors, one copy at a time, to see that
   647  	// the database is still updating correctly.
   648  	var secondIteration bool
   649  	var targetedRoots []crypto.Hash
   650  	for sectorRoot, expiryHeights := range sectorUsageMap {
   651  		if len(expiryHeights) < 2 {
   652  			continue
   653  		}
   654  		targetedRoots = append(targetedRoots, sectorRoot)
   655  		// Break on the second iteration.
   656  		if secondIteration {
   657  			break
   658  		}
   659  		secondIteration = true
   660  	}
   661  	// Remove, one piece at a time, the two targeted sectors.
   662  	for i, root := range targetedRoots {
   663  		// Grab the initial remaining storage, to make sure that it's not being
   664  		// changed when one instance of a repeated sector is removed.
   665  		_, initialRemainingStorage := smt.sm.capacity()
   666  
   667  		// Remove the heights one at a time.
   668  		expiryHeights := sectorUsageMap[root]
   669  		for len(expiryHeights) > 0 {
   670  			// Check that the remaining storage is still the same.
   671  			_, remainingStorage := smt.sm.capacity()
   672  			if remainingStorage != initialRemainingStorage {
   673  				t.Fatal("manager is changing the amount of storage remaining when removing virtual sectors")
   674  			}
   675  
   676  			// Try to remove the sector using a wildcard expiry height.
   677  			err = smt.sm.RemoveSector(root, expiryHeights[0]+548e6)
   678  			if err != errSectorNotFound {
   679  				t.Fatal(err)
   680  			}
   681  
   682  			// Remove the sector from the manager.
   683  			err = smt.sm.RemoveSector(root, expiryHeights[0])
   684  			if err != nil {
   685  				t.Fatal(err)
   686  			}
   687  
   688  			// Check that the filesystem is housing the correct number of
   689  			// sectors.
   690  			infos, err = ioutil.ReadDir(storageFolderOne)
   691  			if err != nil {
   692  				t.Fatal(err)
   693  			}
   694  			infos2, err = ioutil.ReadDir(storageFolderTwo)
   695  			if err != nil {
   696  				t.Fatal(err)
   697  			}
   698  			bonus := 0
   699  			if len(expiryHeights) == 1 {
   700  				// If this is the last expiry height, the sector is no longer
   701  				// viritual and is being removed for real, so we need to
   702  				// subtract it from the expected total number of sectors.
   703  				bonus++
   704  			}
   705  			if len(infos)+len(infos2) != 20-i-bonus {
   706  				t.Fatal("sector count is incorrect while managing virtual sectors")
   707  			}
   708  
   709  			// Update the sector map to reflect the removed sector.
   710  			if len(expiryHeights) > 1 {
   711  				expiryHeights = expiryHeights[1:]
   712  				sectorUsageMap[root] = expiryHeights
   713  			} else {
   714  				expiryHeights = nil
   715  				delete(sectorUsageMap, root)
   716  			}
   717  			err = smt.sectorUsageCheck(sectorUsageMap)
   718  			if err != nil {
   719  				t.Fatal(err)
   720  			}
   721  		}
   722  		// Do a probabilistic reset of the manager, to verify that the
   723  		// persistence structures can reboot without causing issues.
   724  		err = smt.probabilisticReset()
   725  		if err != nil {
   726  			t.Fatal(err)
   727  		}
   728  		// Check that the remaining storage is still the same.
   729  		_, remainingStorage := smt.sm.capacity()
   730  		if remainingStorage != initialRemainingStorage+modules.SectorSize {
   731  			t.Fatal("manager incorrectly updated remaining space when deleting the final height for a sector")
   732  		}
   733  	}
   734  
   735  	// Add a third storage folder.
   736  	prevTotalStorage, prevRemainingStorage := smt.sm.capacity()
   737  	storageFolderThree := filepath.Join(smt.persistDir, "hd3")
   738  	err = os.Mkdir(storageFolderThree, 0700)
   739  	if err != nil {
   740  		t.Fatal(err)
   741  	}
   742  	err = smt.sm.AddStorageFolder(storageFolderThree, minimumStorageFolderSize*2)
   743  	if err != nil {
   744  		t.Fatal(err)
   745  	}
   746  	// Check that the total storage and remaining storage updated correctly.
   747  	totalStorage, remainingStorage = smt.sm.capacity()
   748  	if totalStorage != prevTotalStorage+minimumStorageFolderSize*2 || remainingStorage != prevRemainingStorage+minimumStorageFolderSize*2 {
   749  		t.Fatal("storage folder sizes are not being updated correctly when new storage folders are added")
   750  	}
   751  
   752  	// Add sectors until the storage folders have no more capacity.
   753  	_, remainingStorage = smt.sm.capacity()
   754  	remainingSectors := remainingStorage / modules.SectorSize
   755  	for i := uint64(0); i < remainingSectors; i++ {
   756  		sectorRoot, sectorData, err := createSector()
   757  		if err != nil {
   758  			t.Fatal(err)
   759  		}
   760  		err = smt.sm.AddSector(sectorRoot, 36, sectorData)
   761  		if err != nil {
   762  			t.Fatal(err)
   763  		}
   764  		sectorUsageMap[sectorRoot] = []types.BlockHeight{36}
   765  	}
   766  	// Add another sector, which will not fit in the manager.
   767  	sectorRoot, sectorData, err = createSector()
   768  	if err != nil {
   769  		t.Fatal(err)
   770  	}
   771  	err = smt.sm.AddSector(sectorRoot, 36, sectorData)
   772  	if err != errInsufficientStorageForSector {
   773  		t.Fatal(err)
   774  	}
   775  	// Do a probabilistic reset of the manager, to verify that the persistence
   776  	// structures can reboot without causing issues.
   777  	err = smt.probabilisticReset()
   778  	if err != nil {
   779  		t.Fatal(err)
   780  	}
   781  	_, remainingStorage = smt.sm.capacity()
   782  	if remainingStorage >= modules.SectorSize {
   783  		t.Error("remaining storage is reporting incorrect result - should report that there is not enough room for another sector")
   784  	}
   785  	err = smt.sectorUsageCheck(sectorUsageMap)
   786  	if err != nil {
   787  		t.Fatal(err)
   788  	}
   789  	// Check the filesystem.
   790  	infos, err = ioutil.ReadDir(storageFolderOne)
   791  	if err != nil {
   792  		t.Fatal(err)
   793  	}
   794  	if len(infos) != 8 {
   795  		t.Fatal("expecting 8 sectors in storage folder one")
   796  	}
   797  	infos, err = ioutil.ReadDir(storageFolderTwo)
   798  	if err != nil {
   799  		t.Fatal(err)
   800  	}
   801  	if len(infos) != 24 {
   802  		t.Fatal("expecting 24 sectors in storage folder two")
   803  	}
   804  	infos, err = ioutil.ReadDir(storageFolderThree)
   805  	if err != nil {
   806  		t.Fatal(err)
   807  	}
   808  	if len(infos) != 16 {
   809  		t.Fatal("expecting 16 sectors in storage folder three")
   810  	}
   811  
   812  	// Do some resizing, to cause sectors to be moved around. Every storage
   813  	// folder should have sectors that get moved off of it.
   814  	err = smt.sm.ResizeStorageFolder(1, minimumStorageFolderSize*6)
   815  	if err != nil {
   816  		t.Fatal(err)
   817  	}
   818  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize)
   819  	if err != nil {
   820  		t.Fatal(err)
   821  	}
   822  	err = smt.sm.ResizeStorageFolder(2, minimumStorageFolderSize)
   823  	if err != nil {
   824  		t.Fatal(err)
   825  	}
   826  	err = smt.sm.ResizeStorageFolder(0, minimumStorageFolderSize*6)
   827  	if err != nil {
   828  		t.Fatal(err)
   829  	}
   830  	err = smt.sm.ResizeStorageFolder(1, minimumStorageFolderSize)
   831  	if err != nil {
   832  		t.Fatal(err)
   833  	}
   834  	// Check that all storage folders are reporting successful reads and
   835  	// writes, with no failures.
   836  	for _, sf := range smt.sm.storageFolders {
   837  		if sf.SuccessfulWrites <= 0 || sf.SuccessfulReads <= 0 || sf.FailedWrites > 0 || sf.FailedReads > 0 {
   838  			t.Error("disk stats aren't making sense")
   839  		}
   840  	}
   841  
   842  	// Remove all of the sectors.
   843  	i := 0
   844  	for sectorRoot, expiryHeights := range sectorUsageMap {
   845  		// Grab the initial remaining storage, to make sure that it's not being
   846  		// changed when one instance of a repeated sector is removed.
   847  		_, initialRemainingStorage := smt.sm.capacity()
   848  
   849  		// Remove the heights one at a time.
   850  		for j := range expiryHeights {
   851  			// Check that the remaining storage is still the same.
   852  			_, remainingStorage := smt.sm.capacity()
   853  			if remainingStorage != initialRemainingStorage {
   854  				t.Fatal("manager is changing the amount of storage remaining when removing virtual sectors")
   855  			}
   856  
   857  			// Remove the sector from the manager.
   858  			err = smt.sm.RemoveSector(sectorRoot, expiryHeights[j])
   859  			if err != nil {
   860  				t.Fatal(err)
   861  			}
   862  
   863  			// Check that the filesystem is housing the correct number of
   864  			// sectors.
   865  			infos, err := ioutil.ReadDir(storageFolderOne)
   866  			if err != nil {
   867  				t.Fatal(err)
   868  			}
   869  			infos2, err := ioutil.ReadDir(storageFolderTwo)
   870  			if err != nil {
   871  				t.Fatal(err)
   872  			}
   873  			infos3, err := ioutil.ReadDir(storageFolderThree)
   874  			if err != nil {
   875  				t.Fatal(err)
   876  			}
   877  			bonus := 0
   878  			if j == len(expiryHeights)-1 {
   879  				// If this is the last expiry height, the sector is no longer
   880  				// viritual and is being removed for real, so we need to
   881  				// subtract it from the expected total number of sectors.
   882  				bonus++
   883  			}
   884  			if len(infos)+len(infos2)+len(infos3) != 48-i-bonus {
   885  				t.Error(len(infos)+len(infos2)+len(infos3), i, bonus)
   886  				t.Fatal("sector count is incorrect while managing virtual sectors")
   887  			}
   888  		}
   889  		// Do a probabilistic reset of the manager, to verify that the
   890  		// persistence structures can reboot without causing issues.
   891  		err = smt.probabilisticReset()
   892  		if err != nil {
   893  			t.Fatal(err)
   894  		}
   895  		// Check that the remaining storage is still the same.
   896  		_, remainingStorage := smt.sm.capacity()
   897  		if remainingStorage != initialRemainingStorage+modules.SectorSize {
   898  			t.Fatal("manager incorrectly updated remaining space when deleting the final height for a sector")
   899  		}
   900  		i++
   901  	}
   902  	// Check that all storage folders have successful writes, and no failed
   903  	// reads or writes.
   904  	for _, sf := range smt.sm.storageFolders {
   905  		if sf.SuccessfulWrites <= 0 || sf.SuccessfulReads <= 0 || sf.FailedWrites > 0 || sf.FailedReads > 0 {
   906  			t.Error("disk stats aren't making sense")
   907  		}
   908  	}
   909  
   910  	// Remove all of the storage folders.
   911  	for i := 0; i < 3; i++ {
   912  		err = smt.sm.RemoveStorageFolder(0, false)
   913  		if err != nil {
   914  			t.Fatal(err)
   915  		}
   916  	}
   917  	// Check the filesystem, there should be 3 files in the manager folder
   918  	// (storagemanager.db, storagemanager.json, storagemanager.log).
   919  	infos, err = ioutil.ReadDir(smt.sm.persistDir)
   920  	if err != nil {
   921  		t.Fatal(err)
   922  	}
   923  	if len(infos) != 3 {
   924  		t.Error("unexpected number of files in the manager directory")
   925  	}
   926  }