Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete faster file cache #219

Merged
merged 89 commits into from
Jun 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
ee3d7ad
Added check on Atime on item to condition
Dabnsky Feb 1, 2024
6391dd5
WIP nested if to check Atime
Dabnsky Feb 1, 2024
5934cef
Revert "WIP nested if to check Atime"
Dabnsky Feb 13, 2024
df7a8f3
Revert "Added check on Atime on item to condition"
Dabnsky Feb 13, 2024
dafa996
added debug lines in Read() and Write()
Dabnsky Mar 5, 2024
8483de9
WIP Moved file_cache : Openfile call to Read() and Write()
Dabnsky Mar 18, 2024
bd0b50b
WIP Removed open() call in Write(). Troubleshooting file interface la…
Dabnsky Mar 18, 2024
3edf430
Revert "WIP Removed open() call in Write(). Troubleshooting file inte…
Dabnsky Apr 29, 2024
771c329
Revert "WIP Moved file_cache : Openfile call to Read() and Write()"
Dabnsky Apr 29, 2024
b0f3457
Merge commit 'c16b7102d8cc5d1ecbc7e0a386a54fdd887c4322' into delete-f…
Dabnsky Apr 29, 2024
dc3640d
modified ReadInBuffer() to recieve handleID and load handleID
Dabnsky May 15, 2024
8aa6960
Revert "modified ReadInBuffer() to recieve handleID and load handleID"
Dabnsky May 21, 2024
35712ba
set up download() and getHandleData()
Dabnsky May 22, 2024
1ea5471
added call to getHandleData() from ReadInBuffer()
Dabnsky May 22, 2024
cacc0b3
added call to getHandleData() in WriteFile()
Dabnsky May 23, 2024
08ac9d9
removed redundant fileObj nil check
Dabnsky May 23, 2024
cdd2d77
added additional handlemap.Handle var to replace options.Handle for R…
Dabnsky May 23, 2024
b5d1dfc
added additional handlemap.Handle var to replace options.Handle for W…
Dabnsky May 23, 2024
e58cc12
updated println() into log.Err and log.Debug
Dabnsky May 23, 2024
a47c5c3
Merge remote-tracking branch 'origin/main' into delete-faster-fileCache
Dabnsky May 23, 2024
246bcf7
-changed test to call DownloadFile()
Dabnsky May 23, 2024
0a69842
added DownloadFileOptions struct and adjusted function parameters and…
Dabnsky May 24, 2024
0719b0e
chanced condition to be more specific for new handle produced by Open…
Dabnsky May 24, 2024
9cfec2a
get handle out of download in TruncateFile() to fix test
Dabnsky May 24, 2024
963954a
adjusted TestSyncFile(), WriteFile(), and ReadInBuffer() to hold flag…
Dabnsky May 25, 2024
bc04cdc
Added handle storing and retrieval in TestReadFileWithRefresh()
Dabnsky May 28, 2024
64a0d6b
added sync.mutex thread safe logic into getHandleData()
Dabnsky May 29, 2024
ef2234f
added download logic into ReadFile()
Dabnsky May 29, 2024
a434994
Merge commit '49a17527966091cb3b57d1da6c9805bfa22ac519' into delete-f…
Dabnsky May 29, 2024
40e2ae4
removed download portion of ReadFile()
Dabnsky May 29, 2024
be9ecbe
-removed extra handle being used to swap into handlemap
Dabnsky May 30, 2024
d66e3d9
used sync.RWMutex from handle to lock and unlock handle instead of fi…
Dabnsky May 30, 2024
9828186
-moved getHandleData() logic into DownloadFile()
Dabnsky Jun 3, 2024
d9876b6
-moved download logic into a downloadRequired conditional
Dabnsky Jun 3, 2024
e35e2cd
-used OpenFile() instead of creating a new handle direclty
Dabnsky Jun 3, 2024
b68acdd
added error handle to OpenFile() call in TruncateFile()
Dabnsky Jun 3, 2024
fcf0373
fixed parameters calling DownloadFile()
Dabnsky Jun 3, 2024
83e8fcc
-set default values for flag and mode for DownloadFile()
Dabnsky Jun 3, 2024
bea8da5
added another 12 second wait for file to expire in TestReadFileWithRe…
Dabnsky Jun 4, 2024
a458420
put os.O_RDWR back into OpenFile() call in TruncateFile() to allow Fl…
Dabnsky Jun 4, 2024
fb07973
moved fileExists assigned from isDownloadRequired() in DownlaodFile()
Dabnsky Jun 4, 2024
235e6fa
added error handle in WriteFile()
Dabnsky Jun 4, 2024
3e9238e
spell correction on error string
Dabnsky Jun 4, 2024
80f21da
Added isDownloadNeeded value in handle and adjusted download conditio…
Dabnsky Jun 4, 2024
d7c1b16
removed redundant code from OpenFile().
Dabnsky Jun 5, 2024
a35a18b
condensed values in handle into a single value. changed conditions ch…
Dabnsky Jun 5, 2024
adaf904
fix typo in DownlaodFile() log
Dabnsky Jun 6, 2024
cc4f0f7
renamed rename flagModeStruct to openFileOptions in DownloadFile()
Dabnsky Jun 6, 2024
a966cdf
renamed flag to flags in DownloadFile()
Dabnsky Jun 6, 2024
00b832b
removed DownloadFileOptions struct. refactored all function calls
Dabnsky Jun 6, 2024
e001990
adjusted function call parameters in tests for DownloadFile()
Dabnsky Jun 6, 2024
ff2da1c
commented out debug log for Read() and Write()
Dabnsky Jun 6, 2024
b74de0c
replaced value adjustment of handle with removing single fileFlagMode…
Dabnsky Jun 6, 2024
ff7a6b5
renamed flag to flags for value struct set in OpenFile()
Dabnsky Jun 6, 2024
844fb09
renamed flag to flags in type assertion in DownloadFile()
Dabnsky Jun 6, 2024
b4c5992
moved openFileOptions to it's own defined struct and refactored code …
Dabnsky Jun 6, 2024
06842fb
unexported DownloadFile() and is now downloadFile()
Dabnsky Jun 6, 2024
2540776
WIP add downloadFile() call after openFile In tests to resolve pipeline
Dabnsky Jun 6, 2024
447584d
removed var redefine for handle in TruncateFile()
Dabnsky Jun 6, 2024
87b126a
removed handle redefine assignment for downloadFile() call in ReadInB…
Dabnsky Jun 6, 2024
f374a1f
add downlaodFile() after OpenFile() in TestRenameFileInCache()
Dabnsky Jun 6, 2024
c6a966c
added error check to downloadFile() call in TestRenameFileAndCacheCle…
Dabnsky Jun 6, 2024
f9e6457
Added downloadFile() call to test with error handle.
Dabnsky Jun 6, 2024
c20cabc
cleaned up empty lines
Dabnsky Jun 10, 2024
158d041
removed handlemap.Handle return from downloadFile
Dabnsky Jun 10, 2024
cb60394
adjusted type assertion to maintain consistent style
Dabnsky Jun 10, 2024
6e28d3d
changed "fileFlagMode" key to "openFileOptions" for value in handle
Dabnsky Jun 10, 2024
d1527cd
deleted fileCacheStatsCollector from OpenFile
Dabnsky Jun 10, 2024
eb2d029
corrected comment in TruncateFile
Dabnsky Jun 10, 2024
7d9cfd4
corrected return type var assignment in TestChownInCache()
Dabnsky Jun 10, 2024
61b2318
added value check for handle in CloseFile()
Dabnsky Jun 10, 2024
a83cc3c
fixed var assignemnts when calling downloadFile
Dabnsky Jun 10, 2024
d2486c6
adjsuted var assignements when calling downloadFile
Dabnsky Jun 10, 2024
a4e1366
changed commment and removed akward wording
Dabnsky Jun 11, 2024
150b089
moved handle lock into downloadFile
Dabnsky Jun 11, 2024
3af706f
move handle value check further up CloseFile
Dabnsky Jun 11, 2024
704b134
remove var err error declarations from ReadInBuffer and WriteFile
Dabnsky Jun 11, 2024
7ac582f
changed "FileCache::download" in erro stiring to "FileCache::download…
Dabnsky Jun 11, 2024
b00dce9
removed "for' from error message in ReadInBuffer
Dabnsky Jun 11, 2024
c9f1846
adjusted Error log to reflect function called
Dabnsky Jun 11, 2024
1bef051
put the TestFileCacheTestSuite back in file_cache_test.go
Dabnsky Jun 11, 2024
e927ccd
put ReadFile() call back in place for TestReadFile()
Dabnsky Jun 12, 2024
6b07bbb
adjusted assertions for handle not being nil in TestHardLimitOnSize
Dabnsky Jun 12, 2024
00dcbb9
removed comments in test code
Dabnsky Jun 12, 2024
d047eeb
Moved hard limit check from downloadFile to OpenFile
Dabnsky Jun 12, 2024
fa6dce7
Added ReadFile calls and respecitve asserts in tests.
Dabnsky Jun 12, 2024
7bb87b3
removed extra ReadFile and download calls in tests and added download…
Dabnsky Jun 12, 2024
a1253aa
added error check for ReadFile
Dabnsky Jun 12, 2024
7f3b4f0
Merge commit '47ae7d84f4f607c6a19e6b443000a54fa15a4f0f' into delete-f…
Dabnsky Jun 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
194 changes: 129 additions & 65 deletions component/file_cache/file_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ type FileCacheOptions struct {
HardLimit bool `config:"hard-limit" yaml:"hard-limit,omitempty"`
}

type openFileOptions struct {
flags int
fMode fs.FileMode
}

const (
compName = "file_cache"
defaultMaxEviction = 5000
Expand Down Expand Up @@ -691,136 +696,130 @@ func (fc *FileCache) DeleteFile(options internal.DeleteFileOptions) error {
return nil
}

// OpenFile: Makes the file available in the local cache for further file operations.
func (fc *FileCache) OpenFile(options internal.OpenFileOptions) (*handlemap.Handle, error) {
log.Trace("FileCache::OpenFile : name=%s, flags=%d, mode=%s", options.Name, options.Flags, options.Mode)
func (fc *FileCache) downloadFile(handle *handlemap.Handle) error {
log.Trace("FileCache::downloadFile : name=%s", handle.Path)

localPath := common.JoinUnixFilepath(fc.tmpPath, options.Name)
handle.Lock()
defer handle.Unlock()

//extract flags and mode out of the value from handle
var flags int
var fMode fs.FileMode
val, found := handle.GetValue("openFileOptions")
if !found {
return nil
}
fileOptions := val.(openFileOptions)
flags = fileOptions.flags
fMode = fileOptions.fMode

localPath := common.JoinUnixFilepath(fc.tmpPath, handle.Path)
var f *os.File
var err error

flock := fc.fileLocks.Get(options.Name)
flock := fc.fileLocks.Get(handle.Path)
flock.Lock()
defer flock.Unlock()
Dabnsky marked this conversation as resolved.
Show resolved Hide resolved

fc.policy.CacheValid(localPath)
downloadRequired, fileExists, attr, err := fc.isDownloadRequired(localPath, options.Name, flock)

// return err in case of authorization permission mismatch
if err != nil && err == syscall.EACCES {
return nil, err
downloadRequired, fileExists, attr, err := fc.isDownloadRequired(localPath, handle.Path, flock)
if err != nil {
log.Err("FileCache::downloadFile : Failed to check if download is required for %s [%s]", handle.Path, err.Error())
}

fileMode := fc.defaultPermission
if downloadRequired {
log.Debug("FileCache::OpenFile : Need to download %s", options.Name)
log.Debug("FileCache::downloadFile : Need to download %s", handle.Path)

fileSize := int64(0)
if attr != nil {
fileSize = int64(attr.Size)
}

if fileExists {
log.Debug("FileCache::OpenFile : Delete cached file %s", options.Name)
log.Debug("FileCache::downloadFile : Delete cached file %s", handle.Path)

err := deleteFile(localPath)
if err != nil && !os.IsNotExist(err) {
log.Err("FileCache::OpenFile : Failed to delete old file %s", options.Name)
log.Err("FileCache::downloadFile : Failed to delete old file %s", handle.Path)
}
} else {
// Create the file if if doesn't already exist.
err := os.MkdirAll(filepath.Dir(localPath), fc.defaultPermission)
if err != nil {
log.Err("FileCache::OpenFile : error creating directory structure for file %s [%s]", options.Name, err.Error())
return nil, err
log.Err("FileCache::downloadFile : error creating directory structure for file %s [%s]", handle.Path, err.Error())
return err
}
}

// Open the file in write mode.
f, err = common.OpenFile(localPath, os.O_CREATE|os.O_RDWR, options.Mode)
f, err = common.OpenFile(localPath, os.O_CREATE|os.O_RDWR, fMode)
if err != nil {
log.Err("FileCache::OpenFile : error creating new file %s [%s]", options.Name, err.Error())
return nil, err
log.Err("FileCache::downloadFile : error creating new file %s [%s]", handle.Path, err.Error())
return err
}

if options.Flags&os.O_TRUNC != 0 {
if flags&os.O_TRUNC != 0 {
fileSize = 0
}

if fileSize > 0 {
if fc.diskHighWaterMark != 0 {
currSize, err := common.GetUsage(fc.tmpPath)
if err != nil {
log.Err("FileCache::OpenFile : error getting current usage of cache [%s]", err.Error())
} else {
if (currSize + float64(fileSize)) > fc.diskHighWaterMark {
log.Err("FileCache::OpenFile : cache size limit reached [%f] failed to open %s", fc.maxCacheSize, options.Name)
return nil, syscall.ENOSPC
}
}

}
// Download/Copy the file from storage to the local file.
// We pass a count of 0 to get the entire object
err = fc.NextComponent().CopyToFile(
internal.CopyToFileOptions{
Name: options.Name,
Name: handle.Path,
Offset: 0,
Count: 0,
File: f,
})
if err != nil {
// File was created locally and now download has failed so we need to delete it back from local cache
log.Err("FileCache::OpenFile : error downloading file from storage %s [%s]", options.Name, err.Error())
log.Err("FileCache::downloadFile : error downloading file from storage %s [%s]", handle.Path, err.Error())
_ = f.Close()
_ = os.Remove(localPath)
return nil, err
return err
}
}

// Update the last download time of this file
flock.SetDownloadTime()

log.Debug("FileCache::OpenFile : Download of %s is complete", options.Name)
log.Debug("FileCache::downloadFile : Download of %s is complete", handle.Path)
f.Close()

// After downloading the file, update the modified times and mode of the file.
fileMode := fc.defaultPermission
if attr != nil && !attr.IsModeDefault() {
fileMode = attr.Mode
}
}

// If user has selected some non default mode in config then every local file shall be created with that mode only
err = os.Chmod(localPath, fileMode)
if err != nil {
log.Err("FileCache::OpenFile : Failed to change mode of file %s [%s]", options.Name, err.Error())
}
// TODO: When chown is supported should we update that?
// If user has selected some non default mode in config then every local file shall be created with that mode only
err = os.Chmod(localPath, fileMode)
if err != nil {
log.Err("FileCache::downloadFile : Failed to change mode of file %s [%s]", handle.Path, err.Error())
}
// TODO: When chown is supported should we update that?

if attr != nil {
// chtimes shall be the last api otherwise calling chmod/chown will update the last change time
err = os.Chtimes(localPath, attr.Atime, attr.Mtime)
if err != nil {
log.Err("FileCache::OpenFile : Failed to change times of file %s [%s]", options.Name, err.Error())
}
if attr != nil {
// chtimes shall be the last api otherwise calling chmod/chown will update the last change time
err = os.Chtimes(localPath, attr.Atime, attr.Mtime)
if err != nil {
log.Err("FileCache::downloadFile : Failed to change times of file %s [%s]", handle.Path, err.Error())
}

fileCacheStatsCollector.UpdateStats(stats_manager.Increment, dlFiles, (int64)(1))
} else {
log.Debug("FileCache::OpenFile : %s will be served from cache", options.Name)
fileCacheStatsCollector.UpdateStats(stats_manager.Increment, cacheServed, (int64)(1))
}

fileCacheStatsCollector.UpdateStats(stats_manager.Increment, dlFiles, (int64)(1))

// Open the file and grab a shared lock to prevent deletion by the cache policy.
f, err = common.OpenFile(localPath, options.Flags, options.Mode)
f, err = common.OpenFile(localPath, flags, fMode)
if err != nil {
log.Err("FileCache::OpenFile : error opening cached file %s [%s]", options.Name, err.Error())
return nil, err
log.Err("FileCache::downloadFile : error opening cached file %s [%s]", handle.Path, err.Error())
return err
}

// Increment the handle count in this lock item as there is one handle open for this now
flock.Inc()

handle := handlemap.NewHandle(options.Name)
inf, err := f.Stat()
if err == nil {
handle.Size = inf.Size()
Expand All @@ -831,16 +830,62 @@ func (fc *FileCache) OpenFile(options internal.OpenFileOptions) (*handlemap.Hand
handle.Flags.Set(handlemap.HandleFlagCached)
}

log.Info("FileCache::OpenFile : file=%s, fd=%d", options.Name, f.Fd())
log.Info("FileCache::downloadFile : file=%s, fd=%d", handle.Path, f.Fd())
handle.SetFileObject(f)

//set boolean in isDownloadNeeded value to signal that the file has been downloaded
handle.RemoveValue("openFileOptions")

return nil
}

// OpenFile: Makes the file available in the local cache for further file operations.
func (fc *FileCache) OpenFile(options internal.OpenFileOptions) (*handlemap.Handle, error) {
log.Trace("FileCache::OpenFile : name=%s, flags=%d, mode=%s", options.Name, options.Flags, options.Mode)

attr, err := fc.NextComponent().GetAttr(internal.GetAttrOptions{Name: options.Name})

// return err in case of authorization permission mismatch
if err != nil && err == syscall.EACCES {
return nil, err
}

fileSize := int64(0)
if attr != nil {
fileSize = int64(attr.Size)
}

if fileSize > 0 {
if fc.diskHighWaterMark != 0 {
currSize, err := common.GetUsage(fc.tmpPath)
if err != nil {
log.Err("FileCache::OpenFile : error getting current usage of cache [%s]", err.Error())
} else {
if (currSize + float64(fileSize)) > fc.diskHighWaterMark {
log.Err("FileCache::OpenFile : cache size limit reached [%f] failed to open %s", fc.maxCacheSize, options.Name)
return nil, syscall.ENOSPC
}
}
}
}

// create handle and set value
handle := handlemap.NewHandle(options.Name)
handle.SetValue("openFileOptions", openFileOptions{flags: options.Flags, fMode: options.Mode})

return handle, nil
}

// CloseFile: Flush the file and invalidate it from the cache.
func (fc *FileCache) CloseFile(options internal.CloseFileOptions) error {
log.Trace("FileCache::CloseFile : name=%s, handle=%d", options.Handle.Path, options.Handle.ID)

// if file has not been interactively read or written to by end user, then there is no cached file to close.
_, found := options.Handle.GetValue("openFileOptions")
if found {
return nil
}

localPath := common.JoinUnixFilepath(fc.tmpPath, options.Handle.Path)

err := fc.FlushFile(internal.FlushFileOptions{Handle: options.Handle}) //nolint
Expand Down Expand Up @@ -891,6 +936,12 @@ func (fc *FileCache) ReadFile(options internal.ReadFileOptions) ([]byte, error)
localPath := common.JoinUnixFilepath(fc.tmpPath, options.Handle.Path)
fc.policy.CacheValid(localPath)

err := fc.downloadFile(options.Handle)
if err != nil {
log.Err("FileCache::ReadFile : error from calling downloadFile for %s", options.Handle.Path)
return nil, err
}

f := options.Handle.GetFileObject()
if f == nil {
log.Err("FileCache::ReadFile : error [couldn't find fd in handle] %s", options.Handle.Path)
Expand Down Expand Up @@ -920,6 +971,11 @@ func (fc *FileCache) ReadInBuffer(options internal.ReadInBufferOptions) (int, er
// The file should already be in the cache since CreateFile/OpenFile was called before and a shared lock was acquired.
// log.Debug("FileCache::ReadInBuffer : Reading %v bytes from %s", len(options.Data), options.Handle.Path)

err := fc.downloadFile(options.Handle)
if err != nil {
return 0, fmt.Errorf("error downloading file %s [%s]", options.Handle.Path, err)
}

f := options.Handle.GetFileObject()
if f == nil {
log.Err("FileCache::ReadInBuffer : error [couldn't find fd in handle] %s", options.Handle.Path)
Expand Down Expand Up @@ -950,6 +1006,11 @@ func (fc *FileCache) WriteFile(options internal.WriteFileOptions) (int, error) {
// The file should already be in the cache since CreateFile/OpenFile was called before and a shared lock was acquired.
//log.Debug("FileCache::WriteFile : Writing %v bytes from %s", len(options.Data), options.Handle.Path)

err := fc.downloadFile(options.Handle)
if err != nil {
return 0, fmt.Errorf("error downloading file for %s [%s]", options.Handle.Path, err)
}

f := options.Handle.GetFileObject()
if f == nil {
log.Err("FileCache::WriteFile : error [couldn't find fd in handle] %s", options.Handle.Path)
Expand Down Expand Up @@ -983,7 +1044,6 @@ func (fc *FileCache) WriteFile(options internal.WriteFileOptions) (int, error) {
if err == nil {
// Mark the handle dirty so the file is written back to storage on FlushFile.
options.Handle.Flags.Set(handlemap.HandleFlagDirty)

} else {
log.Err("FileCache::WriteFile : failed to write %s [%s]", options.Handle.Path, err.Error())
}
Expand Down Expand Up @@ -1255,9 +1315,8 @@ func (fc *FileCache) TruncateFile(options internal.TruncateFileOptions) error {
}
}

var h *handlemap.Handle = nil
var err error = nil

var h *handlemap.Handle
var err error
if options.Size == 0 {
// If size is 0 then no need to download any file we can just create an empty file
h, err = fc.CreateFile(internal.CreateFileOptions{Name: options.Name, Mode: fc.defaultPermission})
Expand All @@ -1267,10 +1326,15 @@ func (fc *FileCache) TruncateFile(options internal.TruncateFileOptions) error {
}
} else {
// If size is not 0 then we need to open the file and then truncate it
// Open will force download if file was not present in local system
// downloadFile will download if file was not present in local system
h, err = fc.OpenFile(internal.OpenFileOptions{Name: options.Name, Flags: os.O_RDWR, Mode: fc.defaultPermission})
if err != nil {
log.Err("FileCache::TruncateFile : Error opening file %s [%s]", options.Name, err.Error())
log.Err("FileCache::TruncateFile : Error calling OpenFile with %s [%s]", options.Name, err.Error())
}

err = fc.downloadFile(h)
if err != nil {
log.Err("FileCache::TruncateFile : Error calling downloadFile with %s [%s]", options.Name, err.Error())
return err
}
}
Expand Down
4 changes: 3 additions & 1 deletion component/file_cache/file_cache_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,11 @@ func (suite *fileCacheLinuxTestSuite) TestChownInCache() {
createHandle, _ := suite.fileCache.CreateFile(internal.CreateFileOptions{Name: path, Mode: 0777})
suite.fileCache.CloseFile(internal.CloseFileOptions{Handle: createHandle})
openHandle, _ := suite.fileCache.OpenFile(internal.OpenFileOptions{Name: path, Mode: 0777})
err := suite.fileCache.downloadFile(openHandle)
suite.assert.NoError(err)

// Path should be in the file cache
_, err := os.Stat(suite.cache_path + "/" + path)
_, err = os.Stat(suite.cache_path + "/" + path)
suite.assert.True(err == nil || os.IsExist(err))
// Path should be in fake storage
_, err = os.Stat(suite.fake_storage_path + "/" + path)
Expand Down
Loading
Loading