diff options
author | feistel <6742251-feistel@users.noreply.gitlab.com> | 2022-06-25 23:09:09 +0300 |
---|---|---|
committer | feistel <6742251-feistel@users.noreply.gitlab.com> | 2022-07-07 13:59:46 +0300 |
commit | 57195a5ae472878c19dae4b6a65bc37a76e992e4 (patch) | |
tree | 65537d02c52e4be9518e4ca6ebf87001ec94b076 /internal/serving/disk/lazy.go | |
parent | 1f9cc507928429fc0a85eaa504bfca692c56ff2a (diff) |
Open files lazily when serving content
Changelog: performance
Diffstat (limited to 'internal/serving/disk/lazy.go')
-rw-r--r-- | internal/serving/disk/lazy.go | 80 |
1 files changed, 80 insertions, 0 deletions
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) +} |