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
Stop stat
ing files in dir when request context is cancelled
#5129
Comments
I feel like passing request context down to this method and bailing out if it's cancelled should work: func (fsrv *FileServer) serveBrowse(root, dirPath string, w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error {
// ...
listing, err := fsrv.loadDirectoryContents(r.Context(), ...)
// ...
}
func (fsrv *FileServer) directoryListing(ctx context.Context, entries []fs.DirEntry, canGoUp bool, root, urlPath string, repl *caddy.Replacer) browseTemplateContext {
filesToHide := fsrv.transformHidePaths(repl)
var dirCount, fileCount int
fileInfos := []fileInfo{}
for _, entry := range entries {
if err := ctx.Err(); err != nil {
break // or return err?
} but I'm not sure about how this affects the performance: files, err := dir.ReadDir(10000) // TODO: this limit should probably be configurable How does walking the dir with |
Also, I can take over this issue and submit a PR with the solution I outlined above, unless you have something in mind. |
stat
ing files in dir when request context is cancelled
@abdusco Yes, this sounds like a great idea. Would you like to submit a PR then? Your proposal above sounds like how I'd do it. (For some reason I didn't realize you could just check ctx.Err() to see if it was done, I always selected on ctx.Done() but checking the Err() is even easier!) You could compare using WalkDir() if you want, I don't have a network drive setup handy to test with right now. I'd be curious which one is faster! |
Before the fix: func (fsrv *FileServer) directoryListing(ctx context.Context, entries []fs.DirEntry, canGoUp bool, root, urlPath string, repl *caddy.Replacer) browseTemplateContext {
// ...
go func() {
select {
case <-ctx.Done():
fsrv.logger.Error("listing cancelled")
}
}()
for _, entry := range entries {
// if err := ctx.Err(); err != nil {
// break
// }
// ...
}
fsrv.logger.Error("listing finished")
Listing continues for ~30s more. After the fix:
|
One more point of optimization, but it's not so straightforward: Ideally, |
|
Prevents caddy from performing disk IO needlessly when the request is cancelled before the listing is finished. Closes caddyserver#5129
Prevents caddy from performing disk IO needlessly when the request is cancelled before the listing is finished. Closes caddyserver#5129
Prevents caddy from performing disk IO needlessly when the request is cancelled before the listing is finished. Closes #5129
Ah, right, I think I knew that, but forgot. Thank you for the fix. I'd be open to paginating the browse listings, if possible. (Could be a future TODO) |
I am using Caddy to serve a dir using
file_server
middleware. The directory is hosted on a network drive. So,stat
ing files aren't really cheap, because each stat requires performing a network IO, and it adds up when listing a directory with hundreds of files.Caddy doesn't pass down the request context to directory listing
caddy/modules/caddyhttp/fileserver/browse.go
Line 85 in b4e28af
This means if I close the tab before the page loads (before caddy finishes
stat
ing all files in directory), caddy keeps on stating and it slows down the disk access for everything else.caddy/modules/caddyhttp/fileserver/browse.go
Lines 139 to 148 in b4e28af
It should check if context is cancelled, and stop doing needless disk IO.
The text was updated successfully, but these errors were encountered: