gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/filesystem/siadir/siadir.go (about)

     1  package siadir
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"sync"
     8  	"time"
     9  
    10  	"gitlab.com/NebulousLabs/errors"
    11  	"go.sia.tech/siad/modules"
    12  
    13  	"gitlab.com/SkynetLabs/skyd/skymodules"
    14  )
    15  
    16  type (
    17  	// SiaDir contains the metadata information about a renter directory
    18  	SiaDir struct {
    19  		metadata Metadata
    20  
    21  		// path is the path of the SiaDir folder.
    22  		path string
    23  
    24  		// Utility fields
    25  		deleted bool
    26  		deps    modules.Dependencies
    27  		mu      sync.Mutex
    28  	}
    29  
    30  	// Metadata is the metadata that is saved to disk as a .siadir file
    31  	Metadata struct {
    32  		// For each field in the metadata there is an aggregate value and a
    33  		// siadir specific value. If a field has the aggregate prefix it means
    34  		// that the value takes into account all the siafiles and siadirs in the
    35  		// sub tree. The definition of aggregate and siadir specific values is
    36  		// otherwise the same.
    37  		//
    38  		// Health is the health of the most in need siafile that is not stuck
    39  		//
    40  		// LastHealthCheckTime is the oldest LastHealthCheckTime of any of the
    41  		// siafiles in the siadir and is the last time the health was calculated
    42  		// by the health loop
    43  		//
    44  		// MinRedundancy is the minimum redundancy of any of the siafiles in the
    45  		// siadir
    46  		//
    47  		// ModTime is the last time any of the siafiles in the siadir was
    48  		// updated
    49  		//
    50  		// NumFiles is the total number of siafiles in a siadir
    51  		//
    52  		// NumLostFiles is the total number of siafiles in a siadir that are not on
    53  		// disk and have a health of <= 0
    54  		//
    55  		// NumStuckChunks is the sum of all the Stuck Chunks of any of the
    56  		// siafiles in the siadir
    57  		//
    58  		// NumSubDirs is the number of sub-siadirs in a siadir
    59  		//
    60  		// NumUnfinishedFiles is the total number of unfinished siafiles in a
    61  		// siadir
    62  		//
    63  		// Size is the total amount of data stored in the siafiles of the siadir
    64  		//
    65  		// StuckHealth is the health of the most in need siafile in the siadir,
    66  		// stuck or not stuck
    67  
    68  		// The following fields are aggregate values of the siadir. These values are
    69  		// the totals of the siadir and any sub siadirs, or are calculated based on
    70  		// all the values in the subtree
    71  		AggregateHealth              float64   `json:"aggregatehealth"`
    72  		AggregateLastHealthCheckTime time.Time `json:"aggregatelasthealthchecktime"`
    73  		AggregateMinRedundancy       float64   `json:"aggregateminredundancy"`
    74  		AggregateModTime             time.Time `json:"aggregatemodtime"`
    75  		AggregateNumFiles            uint64    `json:"aggregatenumfiles"`
    76  		AggregateNumLostFiles        uint64    `json:"aggregatenumlostfiles"`
    77  		AggregateNumStuckChunks      uint64    `json:"aggregatenumstuckchunks"`
    78  		AggregateNumSubDirs          uint64    `json:"aggregatenumsubdirs"`
    79  		AggregateNumUnfinishedFiles  uint64    `json:"aggregatenumunfinishedfiles"`
    80  		AggregateRemoteHealth        float64   `json:"aggregateremotehealth"`
    81  		AggregateRepairSize          uint64    `json:"aggregaterepairsize"`
    82  		AggregateSize                uint64    `json:"aggregatesize"`
    83  		AggregateStuckHealth         float64   `json:"aggregatestuckhealth"`
    84  		AggregateStuckSize           uint64    `json:"aggregatestucksize"`
    85  
    86  		// Aggregate Skynet Specific Stats
    87  		AggregateSkynetFiles uint64 `json:"aggregateskynetfiles"`
    88  		AggregateSkynetSize  uint64 `json:"aggregateskynetsize"`
    89  
    90  		// The following fields are information specific to the siadir that is not
    91  		// an aggregate of the entire sub directory tree
    92  		Health              float64     `json:"health"`
    93  		LastHealthCheckTime time.Time   `json:"lasthealthchecktime"`
    94  		MinRedundancy       float64     `json:"minredundancy"`
    95  		Mode                os.FileMode `json:"mode"`
    96  		ModTime             time.Time   `json:"modtime"`
    97  		NumFiles            uint64      `json:"numfiles"`
    98  		NumLostFiles        uint64      `json:"numlostfiles"`
    99  		NumStuckChunks      uint64      `json:"numstuckchunks"`
   100  		NumSubDirs          uint64      `json:"numsubdirs"`
   101  		NumUnfinishedFiles  uint64      `json:"numunfinishedfiles"`
   102  		RemoteHealth        float64     `json:"remotehealth"`
   103  		RepairSize          uint64      `json:"repairsize"`
   104  		Size                uint64      `json:"size"`
   105  		StuckHealth         float64     `json:"stuckhealth"`
   106  		StuckSize           uint64      `json:"stucksize"`
   107  
   108  		// Skynet Specific Stats
   109  		SkynetFiles uint64 `json:"skynetfiles"`
   110  		SkynetSize  uint64 `json:"skynetsize"`
   111  
   112  		// Version is the used version of the header file.
   113  		Version string `json:"version"`
   114  	}
   115  )
   116  
   117  // EqualMetadatas compares two Metadatas. If using this function to check
   118  // persistence the time fields should be checked separately  due to how time is
   119  // persisted
   120  func EqualMetadatas(md1, md2 Metadata) (err error) {
   121  	// Check Aggregate Fields
   122  	if md1.AggregateHealth != md2.AggregateHealth {
   123  		err = errors.Compose(err, fmt.Errorf("AggregateHealth not equal, %v and %v", md1.AggregateHealth, md2.AggregateHealth))
   124  	}
   125  	if md1.AggregateLastHealthCheckTime != md2.AggregateLastHealthCheckTime {
   126  		err = errors.Compose(err, fmt.Errorf("AggregateLastHealthCheckTimes not equal, %v and %v", md1.AggregateLastHealthCheckTime, md2.AggregateLastHealthCheckTime))
   127  	}
   128  	if md1.AggregateMinRedundancy != md2.AggregateMinRedundancy {
   129  		err = errors.Compose(err, fmt.Errorf("AggregateMinRedundancy not equal, %v and %v", md1.AggregateMinRedundancy, md2.AggregateMinRedundancy))
   130  	}
   131  	if md1.AggregateModTime != md2.AggregateModTime {
   132  		err = errors.Compose(err, fmt.Errorf("AggregateModTimes not equal, %v and %v", md1.AggregateModTime, md2.AggregateModTime))
   133  	}
   134  	if md1.AggregateNumFiles != md2.AggregateNumFiles {
   135  		err = errors.Compose(err, fmt.Errorf("AggregateNumFiles not equal, %v and %v", md1.AggregateNumFiles, md2.AggregateNumFiles))
   136  	}
   137  	if md1.AggregateNumLostFiles != md2.AggregateNumLostFiles {
   138  		err = errors.Compose(err, fmt.Errorf("AggregateNumLostFiles not equal, %v and %v", md1.AggregateNumLostFiles, md2.AggregateNumLostFiles))
   139  	}
   140  	if md1.AggregateNumStuckChunks != md2.AggregateNumStuckChunks {
   141  		err = errors.Compose(err, fmt.Errorf("AggregateNumStuckChunks not equal, %v and %v", md1.AggregateNumStuckChunks, md2.AggregateNumStuckChunks))
   142  	}
   143  	if md1.AggregateNumSubDirs != md2.AggregateNumSubDirs {
   144  		err = errors.Compose(err, fmt.Errorf("AggregateNumSubDirs not equal, %v and %v", md1.AggregateNumSubDirs, md2.AggregateNumSubDirs))
   145  	}
   146  	if md1.AggregateNumUnfinishedFiles != md2.AggregateNumUnfinishedFiles {
   147  		err = errors.Compose(err, fmt.Errorf("AggregateNumUnfinishedFiles not equal, %v and %v", md1.AggregateNumUnfinishedFiles, md2.AggregateNumUnfinishedFiles))
   148  	}
   149  	if md1.AggregateRemoteHealth != md2.AggregateRemoteHealth {
   150  		err = errors.Compose(err, fmt.Errorf("AggregateRemoteHealth not equal, %v and %v", md1.AggregateRemoteHealth, md2.AggregateRemoteHealth))
   151  	}
   152  	if md1.AggregateRepairSize != md2.AggregateRepairSize {
   153  		err = errors.Compose(err, fmt.Errorf("AggregateRepairSize not equal, %v and %v", md1.AggregateRepairSize, md2.AggregateRepairSize))
   154  	}
   155  	if md1.AggregateSize != md2.AggregateSize {
   156  		err = errors.Compose(err, fmt.Errorf("AggregateSize not equal, %v and %v", md1.AggregateSize, md2.AggregateSize))
   157  	}
   158  	if md1.AggregateStuckHealth != md2.AggregateStuckHealth {
   159  		err = errors.Compose(err, fmt.Errorf("AggregateStuckHealth not equal, %v and %v", md1.AggregateStuckHealth, md2.AggregateStuckHealth))
   160  	}
   161  	if md1.AggregateStuckSize != md2.AggregateStuckSize {
   162  		err = errors.Compose(err, fmt.Errorf("AggregateStuckSize not equal, %v and %v", md1.AggregateStuckSize, md2.AggregateStuckSize))
   163  	}
   164  
   165  	// Aggregate Skynet Fields
   166  	if md1.AggregateSkynetFiles != md2.AggregateSkynetFiles {
   167  		err = errors.Compose(err, fmt.Errorf("AggregateSkynetFiles not equal, %v and %v", md1.AggregateSkynetFiles, md2.AggregateSkynetFiles))
   168  	}
   169  	if md1.AggregateSkynetSize != md2.AggregateSkynetSize {
   170  		err = errors.Compose(err, fmt.Errorf("AggregateSkynetSize not equal, %v and %v", md1.AggregateSkynetSize, md2.AggregateSkynetSize))
   171  	}
   172  
   173  	// Check SiaDir Fields
   174  	if md1.Health != md2.Health {
   175  		err = errors.Compose(err, fmt.Errorf("Healths not equal, %v and %v", md1.Health, md2.Health))
   176  	}
   177  	if md1.LastHealthCheckTime != md2.LastHealthCheckTime {
   178  		err = errors.Compose(err, fmt.Errorf("LastHealthCheckTime not equal, %v and %v", md1.LastHealthCheckTime, md2.LastHealthCheckTime))
   179  	}
   180  	if md1.MinRedundancy != md2.MinRedundancy {
   181  		err = errors.Compose(err, fmt.Errorf("MinRedundancy not equal, %v and %v", md1.MinRedundancy, md2.MinRedundancy))
   182  	}
   183  	if md1.ModTime != md2.ModTime {
   184  		err = errors.Compose(err, fmt.Errorf("ModTime not equal, %v and %v", md1.ModTime, md2.ModTime))
   185  	}
   186  	if md1.NumFiles != md2.NumFiles {
   187  		err = errors.Compose(err, fmt.Errorf("NumFiles not equal, %v and %v", md1.NumFiles, md2.NumFiles))
   188  	}
   189  	if md1.NumLostFiles != md2.NumLostFiles {
   190  		err = errors.Compose(err, fmt.Errorf("NumLostFiles not equal, %v and %v", md1.NumLostFiles, md2.NumLostFiles))
   191  	}
   192  	if md1.NumStuckChunks != md2.NumStuckChunks {
   193  		err = errors.Compose(err, fmt.Errorf("NumStuckChunks not equal, %v and %v", md1.NumStuckChunks, md2.NumStuckChunks))
   194  	}
   195  	if md1.NumSubDirs != md2.NumSubDirs {
   196  		err = errors.Compose(err, fmt.Errorf("NumSubDirs not equal, %v and %v", md1.NumSubDirs, md2.NumSubDirs))
   197  	}
   198  	if md1.NumUnfinishedFiles != md2.NumUnfinishedFiles {
   199  		err = errors.Compose(err, fmt.Errorf("NumUnfinishedFiles not equal, %v and %v", md1.NumUnfinishedFiles, md2.NumUnfinishedFiles))
   200  	}
   201  	if md1.RemoteHealth != md2.RemoteHealth {
   202  		err = errors.Compose(err, fmt.Errorf("RemoteHealth not equal, %v and %v", md1.RemoteHealth, md2.RemoteHealth))
   203  	}
   204  	if md1.RepairSize != md2.RepairSize {
   205  		err = errors.Compose(err, fmt.Errorf("RepairSize not equal, %v and %v", md1.RepairSize, md2.RepairSize))
   206  	}
   207  	if md1.Size != md2.Size {
   208  		err = errors.Compose(err, fmt.Errorf("Sizes not equal, %v and %v", md1.Size, md2.Size))
   209  	}
   210  	if md1.StuckHealth != md2.StuckHealth {
   211  		err = errors.Compose(err, fmt.Errorf("StuckHealth not equal, %v and %v", md1.StuckHealth, md2.StuckHealth))
   212  	}
   213  	if md1.StuckSize != md2.StuckSize {
   214  		err = errors.Compose(err, fmt.Errorf("StuckSize not equal, %v and %v", md1.StuckSize, md2.StuckSize))
   215  	}
   216  
   217  	// Skynet Fields
   218  	if md1.SkynetFiles != md2.SkynetFiles {
   219  		err = errors.Compose(err, fmt.Errorf("SkynetFiles not equal, %v and %v", md1.SkynetFiles, md2.SkynetFiles))
   220  	}
   221  	if md1.SkynetSize != md2.SkynetSize {
   222  		err = errors.Compose(err, fmt.Errorf("SkynetSize not equal, %v and %v", md1.SkynetSize, md2.SkynetSize))
   223  	}
   224  	return
   225  }
   226  
   227  // VerifyMetadataInit verifies that metadata was properly initialized.
   228  func VerifyMetadataInit(md Metadata) error {
   229  	// Check that the modTimes are not Zero
   230  	if md.AggregateModTime.IsZero() {
   231  		return errors.New("AggregateModTime not initialized")
   232  	}
   233  	if md.ModTime.IsZero() {
   234  		return errors.New("ModTime not initialized")
   235  	}
   236  
   237  	// All the rest of the metadata should be default values
   238  	initMetadata := Metadata{
   239  		AggregateHealth:        DefaultDirHealth,
   240  		AggregateMinRedundancy: DefaultDirRedundancy,
   241  		AggregateModTime:       md.AggregateModTime,
   242  		AggregateRemoteHealth:  DefaultDirHealth,
   243  		AggregateStuckHealth:   DefaultDirHealth,
   244  
   245  		Health:        DefaultDirHealth,
   246  		MinRedundancy: DefaultDirRedundancy,
   247  		ModTime:       md.ModTime,
   248  		RemoteHealth:  DefaultDirHealth,
   249  		StuckHealth:   DefaultDirHealth,
   250  	}
   251  
   252  	return EqualMetadatas(md, initMetadata)
   253  }
   254  
   255  // mdPath returns the path of the SiaDir's metadata on disk.
   256  func (sd *SiaDir) mdPath() string {
   257  	return filepath.Join(sd.path, skymodules.SiaDirExtension)
   258  }
   259  
   260  // Deleted returns the deleted field of the siaDir
   261  func (sd *SiaDir) Deleted() bool {
   262  	sd.mu.Lock()
   263  	defer sd.mu.Unlock()
   264  	return sd.deleted
   265  }
   266  
   267  // Metadata returns the metadata of the SiaDir
   268  func (sd *SiaDir) Metadata() Metadata {
   269  	sd.mu.Lock()
   270  	defer sd.mu.Unlock()
   271  	return sd.metadata
   272  }
   273  
   274  // Path returns the path of the SiaDir on disk.
   275  func (sd *SiaDir) Path() string {
   276  	sd.mu.Lock()
   277  	defer sd.mu.Unlock()
   278  	return sd.path
   279  }
   280  
   281  // MDPath returns the path of the SiaDir's metadata on disk.
   282  func (sd *SiaDir) MDPath() string {
   283  	sd.mu.Lock()
   284  	defer sd.mu.Unlock()
   285  	return sd.mdPath()
   286  }