Line data Source code
1 : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package remote
6 :
7 : import (
8 : "context"
9 : "io"
10 : "os"
11 : "path"
12 :
13 : "github.com/cockroachdb/pebble/vfs"
14 : )
15 :
16 : // NewLocalFS returns a vfs-backed implementation of the remote.Storage
17 : // interface (for testing). All objects will be stored at the directory
18 : // dirname.
19 0 : func NewLocalFS(dirname string, fs vfs.FS) Storage {
20 0 : store := &localFSStore{
21 0 : dirname: dirname,
22 0 : vfs: fs,
23 0 : }
24 0 : return store
25 0 : }
26 :
27 : // localFSStore is a vfs-backed implementation of the remote.Storage
28 : // interface (for testing).
29 : type localFSStore struct {
30 : dirname string
31 : vfs vfs.FS
32 : }
33 :
34 : var _ Storage = (*localFSStore)(nil)
35 :
36 : // Close is part of the remote.Storage interface.
37 0 : func (s *localFSStore) Close() error {
38 0 : *s = localFSStore{}
39 0 : return nil
40 0 : }
41 :
42 : // ReadObject is part of the remote.Storage interface.
43 : func (s *localFSStore) ReadObject(
44 : ctx context.Context, objName string,
45 0 : ) (_ ObjectReader, objSize int64, _ error) {
46 0 : f, err := s.vfs.Open(path.Join(s.dirname, objName))
47 0 : if err != nil {
48 0 : return nil, 0, err
49 0 : }
50 0 : stat, err := f.Stat()
51 0 : if err != nil {
52 0 : return nil, 0, err
53 0 : }
54 :
55 0 : return &localFSReader{f}, stat.Size(), nil
56 : }
57 :
58 : type localFSReader struct {
59 : file vfs.File
60 : }
61 :
62 : var _ ObjectReader = (*localFSReader)(nil)
63 :
64 : // ReadAt is part of the shared.ObjectReader interface.
65 0 : func (r *localFSReader) ReadAt(_ context.Context, p []byte, offset int64) error {
66 0 : n, err := r.file.ReadAt(p, offset)
67 0 : // https://pkg.go.dev/io#ReaderAt
68 0 : if err == io.EOF && n == len(p) {
69 0 : return nil
70 0 : }
71 0 : return err
72 : }
73 :
74 : // Close is part of the shared.ObjectReader interface.
75 0 : func (r *localFSReader) Close() error {
76 0 : r.file.Close()
77 0 : r.file = nil
78 0 : return nil
79 0 : }
80 :
81 : // CreateObject is part of the remote.Storage interface.
82 0 : func (s *localFSStore) CreateObject(objName string) (io.WriteCloser, error) {
83 0 : file, err := s.vfs.Create(path.Join(s.dirname, objName), vfs.WriteCategoryUnspecified)
84 0 : return file, err
85 0 : }
86 :
87 : // List is part of the remote.Storage interface.
88 0 : func (s *localFSStore) List(prefix, delimiter string) ([]string, error) {
89 0 : // TODO(josh): For the intended use case of localfs.go of running 'pebble bench',
90 0 : // List can always return <nil, nil>, since this indicates a file has only one ref,
91 0 : // and since `pebble bench` implies running in a single-pebble-instance context.
92 0 : // https://github.com/cockroachdb/pebble/blob/a9a079d4fb6bf4a9ebc52e4d83a76ad4cbf676cb/objstorage/objstorageprovider/shared.go#L292
93 0 : return nil, nil
94 0 : }
95 :
96 : // Delete is part of the remote.Storage interface.
97 0 : func (s *localFSStore) Delete(objName string) error {
98 0 : return s.vfs.Remove(path.Join(s.dirname, objName))
99 0 : }
100 :
101 : // Size is part of the remote.Storage interface.
102 0 : func (s *localFSStore) Size(objName string) (int64, error) {
103 0 : f, err := s.vfs.Open(path.Join(s.dirname, objName))
104 0 : if err != nil {
105 0 : return 0, err
106 0 : }
107 0 : defer f.Close()
108 0 : stat, err := f.Stat()
109 0 : if err != nil {
110 0 : return 0, err
111 0 : }
112 0 : return stat.Size(), nil
113 : }
114 :
115 : // IsNotExistError is part of the remote.Storage interface.
116 0 : func (s *localFSStore) IsNotExistError(err error) bool {
117 0 : return err == os.ErrNotExist
118 0 : }
|