From 57195a5ae472878c19dae4b6a65bc37a76e992e4 Mon Sep 17 00:00:00 2001 From: feistel <6742251-feistel@users.noreply.gitlab.com> Date: Sat, 25 Jun 2022 22:09:09 +0200 Subject: Open files lazily when serving content Changelog: performance --- internal/serving/disk/lazy.go | 80 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 internal/serving/disk/lazy.go (limited to 'internal/serving/disk/lazy.go') diff --git a/internal/serving/disk/lazy.go b/internal/serving/disk/lazy.go new file mode 100644 index 00000000..9b58457c --- /dev/null +++ b/internal/serving/disk/lazy.go @@ -0,0 +1,80 @@ +package disk + +import ( + "context" + "errors" + "fmt" + "io" + + "gitlab.com/gitlab-org/gitlab-pages/internal/vfs" +) + +var ( + // Make sure lazyFile is a vfs.File to support vfs.ServeCompressedFile + // if the file is compressed. + // This should always be satisfied because root.Open always returns + // a vfs.File. + _ vfs.File = &lazyFile{} + + // Make sure lazyFile is a ReadSeeker to support http.ServeContent + // if the file is not compressed. + // Note: lazyFile.Seek only works if the underlying root.Open returns + // a vfs.SeekableFile which is the case if the file is not compressed. + _ io.ReadSeeker = &lazyFile{} + + // ErrInvalidSeeker is returned if lazyFile.Seek is called and the + // underlying file is not seekable + ErrInvalidSeeker = errors.New("file is not seekable") +) + +type lazyFile struct { + f vfs.File + err error + load func() (vfs.File, error) +} + +func lazyOpen(ctx context.Context, root vfs.Root, fullPath string) lazyFile { + lf := lazyFile{ + load: func() (vfs.File, error) { + return root.Open(ctx, fullPath) + }, + } + + return lf +} + +func (lf lazyFile) Read(p []byte) (int, error) { + if lf.f == nil && lf.err == nil { + lf.f, lf.err = lf.load() + } + + if lf.err != nil { + return 0, lf.err + } + + return lf.f.Read(p) +} + +func (lf lazyFile) Close() error { + if lf.f != nil { + return lf.f.Close() + } + + return nil +} + +func (lf lazyFile) Seek(offset int64, whence int) (int64, error) { + if lf.f == nil && lf.err == nil { + lf.f, lf.err = lf.load() + } + + if lf.err != nil { + return 0, lf.err + } + + if sf, ok := lf.f.(io.ReadSeeker); ok { + return sf.Seek(offset, whence) + } + + return 0, fmt.Errorf("unable to seek from %T: %w", lf.f, ErrInvalidSeeker) +} -- cgit v1.2.3