Commit 8269ba49 authored by Christopher Schinnerl's avatar Christopher Schinnerl

single file endpoint should return accurate fileinfo

parent 8c0f59e5
......@@ -70,6 +70,8 @@ func (r *Renter) DirList(siaPath modules.SiaPath) ([]modules.DirectoryInfo, []mo
}
defer r.tg.Done()
// Get utility maps.
offline, goodForRenew, contracts := r.managedContractUtilityMaps()
var dirs []modules.DirectoryInfo
var files []modules.FileInfo
// Get DirectoryInfo
......@@ -108,7 +110,7 @@ func (r *Renter) DirList(siaPath modules.SiaPath) ([]modules.DirectoryInfo, []mo
if err != nil {
return nil, nil, err
}
file, err := r.File(fileSiaPath)
file, err := r.staticFileSet.CachedFileInfo(fileSiaPath, offline, goodForRenew, contracts)
if err != nil {
return nil, nil, err
}
......
......@@ -511,7 +511,7 @@ func (r *Renter) managedRenterContractsAndUtilities(entrys []*siafile.SiaFileSet
}
// Update the cached expiration of the siafiles.
for _, e := range entrys {
e.UpdateExpiration(contracts)
_ = e.Expiration(contracts)
}
return offline, goodForRenew, contracts
}
......
......@@ -2,7 +2,6 @@ package siafile
import (
"encoding/hex"
"math"
"os"
"path/filepath"
"time"
......@@ -188,16 +187,6 @@ func (sf *SiaFile) ChunkSize() uint64 {
return sf.staticChunkSize()
}
// HealthPercentage returns the health in a more human understandable format out
// of 100%
func (md Metadata) HealthPercentage() float64 {
health := math.Max(md.CachedHealth, md.CachedStuckHealth)
dataPieces := md.staticErasureCode.MinPieces()
parityPieces := md.staticErasureCode.NumPieces() - dataPieces
worstHealth := 1 + float64(dataPieces)/float64(parityPieces)
return 100 * ((worstHealth - health) / worstHealth)
}
// LastHealthCheckTime returns the LastHealthCheckTime timestamp of the file
func (sf *SiaFile) LastHealthCheckTime() time.Time {
sf.mu.Lock()
......
......@@ -112,7 +112,7 @@ func (sfs *SiaFileSet) NewFromLegacyData(fd FileData) (*SiaFileSetEntry, error)
}
// Update the cached fields for progress and uploaded bytes.
file.updateUploadProgressAndBytes()
_, _ = file.UploadProgressAndBytes()
return sfse, errors.AddContext(file.saveFile(), "unable to save file")
}
......@@ -233,7 +233,7 @@ func (sf *SiaFile) AddPiece(pk types.SiaPublicKey, chunkIndex, pieceIndex uint64
}
// Update cache.
defer sf.updateUploadProgressAndBytes()
defer sf.UploadProgressAndBytes()
// Get the index of the host in the public key table.
tableIndex := -1
......@@ -376,14 +376,14 @@ func (sf *SiaFile) Save() error {
return sf.saveFile()
}
// UpdateExpiration updates CachedExpiration with the lowest height at which any
// of the file's contracts will expire.
func (sf *SiaFile) UpdateExpiration(contracts map[string]modules.RenterContract) {
// Expiration updates CachedExpiration with the lowest height at which any of
// the file's contracts will expire and returns the new value.
func (sf *SiaFile) Expiration(contracts map[string]modules.RenterContract) types.BlockHeight {
sf.mu.Lock()
defer sf.mu.Unlock()
if len(sf.pubKeyTable) == 0 {
sf.staticMetadata.CachedExpiration = 0
return
return 0
}
lowest := ^types.BlockHeight(0)
......@@ -397,6 +397,7 @@ func (sf *SiaFile) UpdateExpiration(contracts map[string]modules.RenterContract)
}
}
sf.staticMetadata.CachedExpiration = lowest
return lowest
}
// Health calculates the health of the file to be used in determining repair
......@@ -891,21 +892,22 @@ func (sf *SiaFile) goodPieces(chunkIndex int, offlineMap map[string]bool, goodFo
return numPiecesGoodForRenew, numPiecesGoodForUpload
}
// updateUploadProgressAndBytes updates the CachedUploadProgress and
// UploadProgressAndBytes updates the CachedUploadProgress and
// CachedUploadedBytes fields to indicate what percentage of the file has been
// uploaded based on the unique pieces that have been uploaded and also how many
// bytes have been uploaded of that file in total. Note that a file may be
// Available long before UploadProgress reaches 100%.
func (sf *SiaFile) updateUploadProgressAndBytes() {
func (sf *SiaFile) UploadProgressAndBytes() (float64, uint64) {
_, uploaded := sf.uploadedBytes()
if sf.staticMetadata.FileSize == 0 {
// Update cache.
sf.staticMetadata.CachedUploadProgress = 100
return
return 100, uploaded
}
desired := uint64(len(sf.chunks)) * modules.SectorSize * uint64(sf.staticMetadata.staticErasureCode.NumPieces())
// Update cache.
sf.staticMetadata.CachedUploadProgress = math.Min(100*(float64(uploaded)/float64(desired)), 100)
return sf.staticMetadata.CachedUploadProgress, uploaded
}
// uploadedBytes indicates how many bytes of the file have been uploaded via
......
......@@ -759,7 +759,7 @@ func TestFileExpiration(t *testing.T) {
}
f := newBlankTestFile()
contracts := make(map[string]modules.RenterContract)
f.UpdateExpiration(contracts)
_ = f.Expiration(contracts)
if f.staticMetadata.CachedExpiration != 0 {
t.Error("file with no pieces should report as having no time remaining")
}
......@@ -780,7 +780,7 @@ func TestFileExpiration(t *testing.T) {
fc := modules.RenterContract{}
fc.EndHeight = 100
contracts[pk1.String()] = fc
f.UpdateExpiration(contracts)
_ = f.Expiration(contracts)
if f.staticMetadata.CachedExpiration != 100 {
t.Error("file did not report lowest WindowStart")
}
......@@ -788,7 +788,7 @@ func TestFileExpiration(t *testing.T) {
// Add a contract with a lower WindowStart.
fc.EndHeight = 50
contracts[pk2.String()] = fc
f.UpdateExpiration(contracts)
_ = f.Expiration(contracts)
if f.staticMetadata.CachedExpiration != 50 {
t.Error("file did not report lowest WindowStart")
}
......@@ -796,7 +796,7 @@ func TestFileExpiration(t *testing.T) {
// Add a contract with a higher WindowStart.
fc.EndHeight = 75
contracts[pk3.String()] = fc
f.UpdateExpiration(contracts)
_ = f.Expiration(contracts)
if f.staticMetadata.CachedExpiration != 50 {
t.Error("file did not report lowest WindowStart")
}
......
......@@ -207,7 +207,7 @@ func (sfs *SiaFileSet) exists(siaPath modules.SiaPath) bool {
// readLockFileInfo returns information on a siafile. As a performance
// optimization, the fileInfo takes the maps returned by
// renter.managedContractUtilityMaps for many files at once.
func (sfs *SiaFileSet) readLockFileInfo(siaPath modules.SiaPath, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]modules.RenterContract) (modules.FileInfo, error) {
func (sfs *SiaFileSet) readLockCachedFileInfo(siaPath modules.SiaPath, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]modules.RenterContract) (modules.FileInfo, error) {
// Get the file's metadata and its contracts
md, err := sfs.readLockMetadata(siaPath)
if err != nil {
......@@ -232,7 +232,7 @@ func (sfs *SiaFileSet) readLockFileInfo(siaPath modules.SiaPath, offline map[str
Health: md.CachedHealth,
LocalPath: localPath,
MaxHealth: math.Max(md.CachedHealth, md.CachedStuckHealth),
MaxHealthPercent: md.HealthPercentage(),
MaxHealthPercent: healthPercentage(md.CachedHealth, md.CachedStuckHealth, md.staticErasureCode),
ModTime: md.ModTime,
NumStuckChunks: md.NumStuckChunks,
OnDisk: onDisk,
......@@ -245,7 +245,6 @@ func (sfs *SiaFileSet) readLockFileInfo(siaPath modules.SiaPath, offline map[str
UploadedBytes: md.CachedUploadedBytes,
UploadProgress: md.CachedUploadProgress,
}
return fileInfo, nil
}
......@@ -367,10 +366,60 @@ func (sfs *SiaFileSet) Exists(siaPath modules.SiaPath) bool {
func (sfs *SiaFileSet) FileInfo(siaPath modules.SiaPath, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]modules.RenterContract) (modules.FileInfo, error) {
sfs.mu.Lock()
defer sfs.mu.Unlock()
return sfs.readLockFileInfo(siaPath, offline, goodForRenew, contracts)
entry, err := sfs.open(siaPath)
if err != nil {
return modules.FileInfo{}, err
}
defer entry.Close()
// Build the FileInfo
var onDisk bool
localPath := entry.LocalPath()
if localPath != "" {
_, err = os.Stat(localPath)
onDisk = err == nil
}
health, stuckHealth, numStuckChunks := entry.Health(offline, goodForRenew)
redundancy := entry.Redundancy(offline, goodForRenew)
uploadProgress, uploadedBytes := entry.UploadProgressAndBytes()
fileInfo := modules.FileInfo{
AccessTime: entry.AccessTime(),
Available: redundancy >= 1,
ChangeTime: entry.ChangeTime(),
CipherType: entry.MasterKey().Type().String(),
CreateTime: entry.CreateTime(),
Expiration: entry.Expiration(contracts),
Filesize: entry.Size(),
Health: health,
LocalPath: localPath,
MaxHealth: math.Max(health, stuckHealth),
MaxHealthPercent: healthPercentage(health, stuckHealth, entry.ErasureCode()),
ModTime: entry.ModTime(),
NumStuckChunks: numStuckChunks,
OnDisk: onDisk,
Recoverable: onDisk || redundancy >= 1,
Redundancy: redundancy,
Renewing: true,
SiaPath: siaPath.String(),
Stuck: numStuckChunks > 0,
StuckHealth: stuckHealth,
UploadedBytes: uploadedBytes,
UploadProgress: uploadProgress,
}
return fileInfo, nil
}
// FileList returns all of the files that the renter has.
// CachedFileInfo returns a modules.FileInfo for a given file like FileInfo but
// instead of computing redundancy, health etc. it uses cached values.
func (sfs *SiaFileSet) CachedFileInfo(siaPath modules.SiaPath, offline map[string]bool, goodForRenew map[string]bool, contracts map[string]modules.RenterContract) (modules.FileInfo, error) {
sfs.mu.Lock()
defer sfs.mu.Unlock()
return sfs.readLockCachedFileInfo(siaPath, offline, goodForRenew, contracts)
}
// FileList returns all of the files that the renter has. This method will used
// cached values for health, redundancy etc.
func (sfs *SiaFileSet) FileList(offlineMap map[string]bool, goodForRenewMap map[string]bool, contractsMap map[string]modules.RenterContract) ([]modules.FileInfo, error) {
// Guarantee that no other thread is writing to sfs.
sfs.mu.Lock()
......@@ -388,7 +437,7 @@ func (sfs *SiaFileSet) FileList(offlineMap map[string]bool, goodForRenewMap map[
if err := siaPath.LoadSysPath(sfs.siaFileDir, path); err != nil {
continue
}
file, err := sfs.readLockFileInfo(siaPath, offlineMap, goodForRenewMap, contractsMap)
file, err := sfs.readLockCachedFileInfo(siaPath, offlineMap, goodForRenewMap, contractsMap)
if os.IsNotExist(err) || err == ErrUnknownPath {
continue
}
......@@ -498,3 +547,13 @@ func (sfs *SiaFileSet) Rename(siaPath, newSiaPath modules.SiaPath) error {
// Update the siafile to have a new name.
return entry.Rename(newSiaPath, newSiaPath.SiaFileSysPath(sfs.siaFileDir))
}
// healthPercentage returns the health in a more human understandable format out
// of 100%
func healthPercentage(h, sh float64, ec modules.ErasureCoder) float64 {
health := math.Max(h, sh)
dataPieces := ec.MinPieces()
parityPieces := ec.NumPieces() - dataPieces
worstHealth := 1 + float64(dataPieces)/float64(parityPieces)
return 100 * ((worstHealth - health) / worstHealth)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment