Skip resolving paths for symlinks during index if the path isn't a symlink.
godirwalk can inform us if the file currently processed is a symlink or not (which is gathered without extra stat syscalls).Using this information to skip resolving the symlink to the absolute path (which is necessary to get the stat info of the image file instead of the symlink to it) saves on a lot of syscalls. Resolve causes a Stat syscall for each level in the path, which is very expensive and slows down scanning.
This commit is contained in:
parent
c2e1989dad
commit
add115c7e4
|
@ -187,7 +187,15 @@ func (ind *Index) Start(o IndexOptions) fs.Done {
|
|||
return nil
|
||||
}
|
||||
|
||||
mf, err := NewMediaFile(fileName)
|
||||
var mf *MediaFile
|
||||
var err error
|
||||
if isSymlink {
|
||||
mf, err = NewMediaFile(fileName)
|
||||
} else {
|
||||
// If the file found while scanning is not a symlink we can
|
||||
// skip resolving the fileName, which is resource intensive.
|
||||
mf, err = NewMediaFileSkipResolve(fileName, fileName)
|
||||
}
|
||||
|
||||
// Check if file exists and is not empty.
|
||||
if err != nil {
|
||||
|
|
|
@ -36,39 +36,53 @@ import (
|
|||
|
||||
// MediaFile represents a single photo, video or sidecar file.
|
||||
type MediaFile struct {
|
||||
fileName string
|
||||
fileRoot string
|
||||
statErr error
|
||||
modTime time.Time
|
||||
fileSize int64
|
||||
fileType fs.Type
|
||||
mimeType string
|
||||
takenAt time.Time
|
||||
takenAtSrc string
|
||||
hash string
|
||||
checksum string
|
||||
hasJpeg bool
|
||||
noColorProfile bool
|
||||
colorProfile string
|
||||
width int
|
||||
height int
|
||||
metaData meta.Data
|
||||
metaOnce sync.Once
|
||||
fileMutex sync.Mutex
|
||||
location *entity.Cell
|
||||
imageConfig *image.Config
|
||||
fileName string
|
||||
fileNameResolved string
|
||||
fileRoot string
|
||||
statErr error
|
||||
modTime time.Time
|
||||
fileSize int64
|
||||
fileType fs.Type
|
||||
mimeType string
|
||||
takenAt time.Time
|
||||
takenAtSrc string
|
||||
hash string
|
||||
checksum string
|
||||
hasJpeg bool
|
||||
noColorProfile bool
|
||||
colorProfile string
|
||||
width int
|
||||
height int
|
||||
metaData meta.Data
|
||||
metaOnce sync.Once
|
||||
fileMutex sync.Mutex
|
||||
location *entity.Cell
|
||||
imageConfig *image.Config
|
||||
}
|
||||
|
||||
// NewMediaFile returns a new media file.
|
||||
func NewMediaFile(fileName string) (m *MediaFile, err error) {
|
||||
fileNameResolved, err := fs.Resolve(fileName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewMediaFileSkipResolve(fileName, fileNameResolved)
|
||||
}
|
||||
|
||||
// NewMediaFileSkipResolve returns a new media file without resolving symlinks.
|
||||
// This is useful because if it's known fileName is fully resolved it's a lot
|
||||
// faster.
|
||||
func NewMediaFileSkipResolve(fileName string, fileNameResolved string) (m *MediaFile, err error) {
|
||||
// Create struct.
|
||||
m = &MediaFile{
|
||||
fileName: fileName,
|
||||
fileRoot: entity.RootUnknown,
|
||||
fileType: fs.UnknownType,
|
||||
metaData: meta.New(),
|
||||
width: -1,
|
||||
height: -1,
|
||||
fileName: fileName,
|
||||
fileNameResolved: fileNameResolved,
|
||||
fileRoot: entity.RootUnknown,
|
||||
fileType: fs.UnknownType,
|
||||
metaData: meta.New(),
|
||||
width: -1,
|
||||
height: -1,
|
||||
}
|
||||
|
||||
// Check if file exists and is not empty.
|
||||
|
@ -97,14 +111,7 @@ func (m *MediaFile) Stat() (size int64, mod time.Time, err error) {
|
|||
return m.fileSize, m.modTime, m.statErr
|
||||
}
|
||||
|
||||
fileName := m.FileName()
|
||||
|
||||
// Resolve symlinks.
|
||||
if fileName, err = fs.Resolve(fileName); err != nil {
|
||||
m.statErr = err
|
||||
m.modTime = time.Time{}
|
||||
m.fileSize = -1
|
||||
} else if s, err := os.Stat(fileName); err != nil {
|
||||
if s, err := os.Stat(m.fileNameResolved); err != nil {
|
||||
m.statErr = err
|
||||
m.modTime = time.Time{}
|
||||
m.fileSize = -1
|
||||
|
|
Loading…
Reference in a new issue