diff --git a/pkg/builder/file_pool_stats_build_executor_test.go b/pkg/builder/file_pool_stats_build_executor_test.go index b269c82a..622dac62 100644 --- a/pkg/builder/file_pool_stats_build_executor_test.go +++ b/pkg/builder/file_pool_stats_build_executor_test.go @@ -70,12 +70,29 @@ func TestFilePoolStatsBuildExecutorExample(t *testing.T) { } }) + // Mock all the file operations performed in the execution request. + filePool := mock.NewMockFilePool(ctrl) + file1 := mock.NewMockFileReadWriter(ctrl) + file2 := mock.NewMockFileReadWriter(ctrl) + + filePool.EXPECT().NewFile().Return(file1, nil) + file1.EXPECT().Truncate(int64(5)).Return(nil) + file1.EXPECT().Close().Return(nil) + filePool.EXPECT().NewFile().Return(file2, nil) + file2.EXPECT().WriteAt([]byte("Hello"), int64(100)).Return(5, nil) + file2.EXPECT().ReadAt(gomock.Any(), int64(98)).DoAndReturn(func(p []byte, offset int64) (int, error) { + copy(p, []byte("\x00\x00Hello\x00\x00\x00")) + return 7, io.EOF + }) + file2.EXPECT().Truncate(int64(42)).Return(nil) + file2.EXPECT().Close().Return(nil) + // Perform the execution request. executionStateUpdates := make(chan *remoteworker.CurrentState_Executing, 3) buildExecutor := builder.NewFilePoolStatsBuildExecutor(baseBuildExecutor) executeResponse := buildExecutor.Execute( ctx, - filesystem.InMemoryFilePool, + filePool, monitor, digest.MustNewFunction("hello", remoteexecution.DigestFunction_MD5), request, diff --git a/pkg/filesystem/BUILD.bazel b/pkg/filesystem/BUILD.bazel index b41e3db9..88ea5d71 100644 --- a/pkg/filesystem/BUILD.bazel +++ b/pkg/filesystem/BUILD.bazel @@ -6,10 +6,8 @@ go_library( "bitmap_sector_allocator.go", "block_device_backed_file_pool.go", "configuration.go", - "directory_backed_file_pool.go", "empty_file_pool.go", "file_pool.go", - "in_memory_file_pool.go", "lazy_directory.go", "metrics_file_pool.go", "quota_enforcing_file_pool.go", @@ -34,9 +32,7 @@ go_test( srcs = [ "bitmap_sector_allocator_test.go", "block_device_backed_file_pool_test.go", - "directory_backed_file_pool_test.go", "empty_file_pool_test.go", - "in_memory_file_pool_test.go", "lazy_directory_test.go", "quota_enforcing_file_pool_test.go", ], diff --git a/pkg/filesystem/configuration.go b/pkg/filesystem/configuration.go index 94850c56..b244a1c2 100644 --- a/pkg/filesystem/configuration.go +++ b/pkg/filesystem/configuration.go @@ -5,8 +5,6 @@ import ( pb "github.com/buildbarn/bb-remote-execution/pkg/proto/configuration/filesystem" "github.com/buildbarn/bb-storage/pkg/blockdevice" - "github.com/buildbarn/bb-storage/pkg/filesystem" - "github.com/buildbarn/bb-storage/pkg/filesystem/path" "github.com/buildbarn/bb-storage/pkg/util" "google.golang.org/grpc/codes" @@ -25,18 +23,6 @@ func NewFilePoolFromConfiguration(configuration *pb.FilePoolConfiguration) (File var filePool FilePool switch backend := configuration.Backend.(type) { - case *pb.FilePoolConfiguration_InMemory: - filePool = InMemoryFilePool - case *pb.FilePoolConfiguration_DirectoryPath: - directory, err := filesystem.NewLocalDirectory(path.LocalFormat.NewParser(backend.DirectoryPath)) - if err != nil { - return nil, util.StatusWrapf(err, "Failed to open directory %#v", backend.DirectoryPath) - } - if err := directory.RemoveAllChildren(); err != nil { - directory.Close() - return nil, util.StatusWrapf(err, "Failed to empty out directory %#v", backend.DirectoryPath) - } - filePool = NewDirectoryBackedFilePool(directory) case *pb.FilePoolConfiguration_BlockDevice: blockDevice, sectorSizeBytes, sectorCount, err := blockdevice.NewBlockDeviceFromConfiguration(backend.BlockDevice, true) if err != nil { diff --git a/pkg/filesystem/directory_backed_file_pool.go b/pkg/filesystem/directory_backed_file_pool.go deleted file mode 100644 index 2cbe1276..00000000 --- a/pkg/filesystem/directory_backed_file_pool.go +++ /dev/null @@ -1,106 +0,0 @@ -package filesystem - -import ( - "io" - "os" - "strconv" - "sync/atomic" - - "github.com/buildbarn/bb-storage/pkg/filesystem" - "github.com/buildbarn/bb-storage/pkg/filesystem/path" -) - -type directoryBackedFilePool struct { - directory filesystem.Directory - - nextID atomic.Uint64 -} - -// NewDirectoryBackedFilePool creates a FilePool that stores all data -// written to files into a single directory on disk. Files stored in the -// underlying directory are simply identified by an incrementing number. -// -// As many files may exist at a given point in time, this implementation -// does not keep any backing files open. This would exhaust the worker's -// file descriptor table. Files are opened on demand. -// -// TODO: Maybe use an eviction.Set to keep a small number of files open? -func NewDirectoryBackedFilePool(directory filesystem.Directory) FilePool { - return &directoryBackedFilePool{ - directory: directory, - } -} - -func (fp *directoryBackedFilePool) NewFile() (filesystem.FileReadWriter, error) { - return &lazyOpeningSelfDeletingFile{ - directory: fp.directory, - name: path.MustNewComponent(strconv.FormatUint(fp.nextID.Add(1), 10)), - }, nil -} - -// lazyOpeningSelfDeletingFile is a file descriptor that forwards -// operations to a file that is opened on demand. Upon closure, the -// underlying file is unlinked. -type lazyOpeningSelfDeletingFile struct { - directory filesystem.Directory - name path.Component -} - -func (f *lazyOpeningSelfDeletingFile) Close() error { - if err := f.directory.Remove(f.name); err != nil && !os.IsNotExist(err) { - return err - } - return nil -} - -func (f *lazyOpeningSelfDeletingFile) GetNextRegionOffset(off int64, regionType filesystem.RegionType) (int64, error) { - fh, err := f.directory.OpenRead(f.name) - if os.IsNotExist(err) { - // Empty file that doesn't explicitly exist in the - // backing store yet. Treat it as if it's a zero-length - // file. - return 0, io.EOF - } else if err != nil { - return 0, err - } - defer fh.Close() - return fh.GetNextRegionOffset(off, regionType) -} - -func (f *lazyOpeningSelfDeletingFile) ReadAt(p []byte, off int64) (int, error) { - fh, err := f.directory.OpenRead(f.name) - if os.IsNotExist(err) { - // Empty file that doesn't explicitly exist in the - // backing store yet. Treat it as if it's a zero-length - // file. - return 0, io.EOF - } else if err != nil { - return 0, err - } - defer fh.Close() - return fh.ReadAt(p, off) -} - -func (f *lazyOpeningSelfDeletingFile) Sync() error { - // Because FilePool does not provide any persistency, there is - // no need to synchronize any data. - return nil -} - -func (f *lazyOpeningSelfDeletingFile) Truncate(size int64) error { - fh, err := f.directory.OpenWrite(f.name, filesystem.CreateReuse(0o600)) - if err != nil { - return err - } - defer fh.Close() - return fh.Truncate(size) -} - -func (f *lazyOpeningSelfDeletingFile) WriteAt(p []byte, off int64) (int, error) { - fh, err := f.directory.OpenWrite(f.name, filesystem.CreateReuse(0o600)) - if err != nil { - return 0, err - } - defer fh.Close() - return fh.WriteAt(p, off) -} diff --git a/pkg/filesystem/directory_backed_file_pool_test.go b/pkg/filesystem/directory_backed_file_pool_test.go deleted file mode 100644 index 8142ec81..00000000 --- a/pkg/filesystem/directory_backed_file_pool_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package filesystem_test - -import ( - "io" - "syscall" - "testing" - - "github.com/buildbarn/bb-remote-execution/internal/mock" - re_filesystem "github.com/buildbarn/bb-remote-execution/pkg/filesystem" - "github.com/buildbarn/bb-storage/pkg/filesystem" - "github.com/buildbarn/bb-storage/pkg/filesystem/path" - "github.com/stretchr/testify/require" - - "go.uber.org/mock/gomock" -) - -func TestDirectoryBackedFilePool(t *testing.T) { - ctrl := gomock.NewController(t) - - directory := mock.NewMockDirectory(ctrl) - fp := re_filesystem.NewDirectoryBackedFilePool(directory) - - t.Run("EmptyFile", func(t *testing.T) { - f, err := fp.NewFile() - require.NoError(t, err) - - // Underlying file should not yet exist. This should be - // interpreted as if the file is empty. - directory.EXPECT().OpenRead(path.MustNewComponent("1")).Return(nil, syscall.ENOENT) - var p [10]byte - n, err := f.ReadAt(p[:], 0) - require.Equal(t, 0, n) - require.Equal(t, io.EOF, err) - - // GetNextRegionOffset() should behave similarly. - directory.EXPECT().OpenRead(path.MustNewComponent("1")).Return(nil, syscall.ENOENT) - _, err = f.GetNextRegionOffset(0, filesystem.Data) - require.Equal(t, io.EOF, err) - - directory.EXPECT().OpenRead(path.MustNewComponent("1")).Return(nil, syscall.ENOENT) - _, err = f.GetNextRegionOffset(0, filesystem.Hole) - require.Equal(t, io.EOF, err) - - directory.EXPECT().Remove(path.MustNewComponent("1")).Return(syscall.ENOENT) - require.NoError(t, f.Close()) - }) - - t.Run("NonEmptyFile", func(t *testing.T) { - f, err := fp.NewFile() - require.NoError(t, err) - - // Write a piece of text into the file. - fileWriter := mock.NewMockFileWriter(ctrl) - directory.EXPECT().OpenWrite(path.MustNewComponent("2"), filesystem.CreateReuse(0o600)).Return(fileWriter, nil) - fileWriter.EXPECT().WriteAt([]byte("Hello, world"), int64(123)).Return(12, nil) - fileWriter.EXPECT().Close() - n, err := f.WriteAt([]byte("Hello, world"), 123) - require.Equal(t, 12, n) - require.NoError(t, err) - - // Truncate a part of it. - fileWriter = mock.NewMockFileWriter(ctrl) - directory.EXPECT().OpenWrite(path.MustNewComponent("2"), filesystem.CreateReuse(0o600)).Return(fileWriter, nil) - fileWriter.EXPECT().Truncate(int64(128)) - fileWriter.EXPECT().Close() - require.NoError(t, f.Truncate(128)) - - // Read back the end of the file. - fileReader := mock.NewMockFileReader(ctrl) - directory.EXPECT().OpenRead(path.MustNewComponent("2")).Return(fileReader, nil) - fileReader.EXPECT().ReadAt(gomock.Any(), int64(120)).DoAndReturn( - func(p []byte, off int64) (int, error) { - require.Len(t, p, 10) - copy(p, "\x00\x00\x00Hello") - return 8, io.EOF - }) - fileReader.EXPECT().Close() - var p [10]byte - n, err = f.ReadAt(p[:], 120) - require.Equal(t, 8, n) - require.Equal(t, io.EOF, err) - require.Equal(t, []byte("\x00\x00\x00Hello"), p[:8]) - - // Calls for GetNextRegionOffset() should be forwarded. - fileReader = mock.NewMockFileReader(ctrl) - directory.EXPECT().OpenRead(path.MustNewComponent("2")).Return(fileReader, nil) - fileReader.EXPECT().GetNextRegionOffset(int64(0), filesystem.Hole).Return(int64(123), nil) - fileReader.EXPECT().Close() - off, err := f.GetNextRegionOffset(0, filesystem.Hole) - require.NoError(t, err) - require.Equal(t, int64(123), off) - - directory.EXPECT().Remove(path.MustNewComponent("2")).Return(nil) - require.NoError(t, f.Close()) - }) -} diff --git a/pkg/filesystem/in_memory_file_pool.go b/pkg/filesystem/in_memory_file_pool.go deleted file mode 100644 index 04f0c528..00000000 --- a/pkg/filesystem/in_memory_file_pool.go +++ /dev/null @@ -1,81 +0,0 @@ -package filesystem - -import ( - "io" - - "github.com/buildbarn/bb-storage/pkg/filesystem" -) - -type inMemoryFilePool struct{} - -func (fp inMemoryFilePool) NewFile() (filesystem.FileReadWriter, error) { - return &inMemoryFile{}, nil -} - -type inMemoryFile struct { - data []byte -} - -func (f *inMemoryFile) Close() error { - f.data = nil - return nil -} - -func (f *inMemoryFile) GetNextRegionOffset(off int64, regionType filesystem.RegionType) (int64, error) { - // Files are stored in a byte slice contiguously, so there is no - // sparseness. - if off >= int64(len(f.data)) { - return 0, io.EOF - } - switch regionType { - case filesystem.Data: - return off, nil - case filesystem.Hole: - return int64(len(f.data)), nil - default: - panic("Unknown region type") - } -} - -func (f *inMemoryFile) ReadAt(p []byte, off int64) (int, error) { - if int(off) >= len(f.data) { - return 0, io.EOF - } - if n := copy(p, f.data[off:]); n < len(p) { - return n, io.EOF - } - return len(p), nil -} - -func (f *inMemoryFile) Sync() error { - // Because FilePool does not provide any persistency, there is - // no need to synchronize any data. - return nil -} - -func (f *inMemoryFile) Truncate(size int64) error { - if len(f.data) >= int(size) { - // Truncate the file. - f.data = f.data[:size] - } else { - // Grow the file. - f.data = append(f.data, make([]byte, int(size)-len(f.data))...) - } - return nil -} - -func (f *inMemoryFile) WriteAt(p []byte, off int64) (int, error) { - // Zero-sized writes should not cause the file to grow. - if len(p) == 0 { - return 0, nil - } - - if size := int(off) + len(p); len(f.data) < size { - // Grow the file. - f.data = append(f.data, make([]byte, size-len(f.data))...) - } - return copy(f.data[off:], p), nil -} - -// InMemoryFilePool is a FilePool that stores all data in memory. -var InMemoryFilePool FilePool = inMemoryFilePool{} diff --git a/pkg/filesystem/in_memory_file_pool_test.go b/pkg/filesystem/in_memory_file_pool_test.go deleted file mode 100644 index 079b4c91..00000000 --- a/pkg/filesystem/in_memory_file_pool_test.go +++ /dev/null @@ -1,65 +0,0 @@ -package filesystem_test - -import ( - "io" - "testing" - - "github.com/buildbarn/bb-remote-execution/pkg/filesystem" - "github.com/stretchr/testify/require" -) - -func TestInMemoryFilePool(t *testing.T) { - fp := filesystem.InMemoryFilePool - - t.Run("EmptyFile", func(t *testing.T) { - f, err := fp.NewFile() - require.NoError(t, err) - - var p [10]byte - n, err := f.ReadAt(p[:], 0) - require.Equal(t, 0, n) - require.Equal(t, io.EOF, err) - - require.NoError(t, f.Close()) - }) - - t.Run("NonEmptyFile", func(t *testing.T) { - f, err := fp.NewFile() - require.NoError(t, err) - - // Write a piece of text into the file. - n, err := f.WriteAt([]byte("Hello, world"), 123) - require.Equal(t, 12, n) - require.NoError(t, err) - - // Truncate a part of it. - require.NoError(t, f.Truncate(128)) - - // Read back the end of the file. - var p [10]byte - n, err = f.ReadAt(p[:], 120) - require.Equal(t, 8, n) - require.Equal(t, io.EOF, err) - require.Equal(t, []byte("\x00\x00\x00Hello"), p[:8]) - - require.NoError(t, f.Close()) - }) - - t.Run("ZeroSizedWrite", func(t *testing.T) { - f, err := fp.NewFile() - require.NoError(t, err) - - // A zero-sized write should not cause the file to - // actually grow. The read should still return EOF. - n, err := f.WriteAt(nil, 123) - require.Equal(t, 0, n) - require.NoError(t, err) - - var p [10]byte - n, err = f.ReadAt(p[:], 0) - require.Equal(t, 0, n) - require.Equal(t, io.EOF, err) - - require.NoError(t, f.Close()) - }) -} diff --git a/pkg/proto/configuration/filesystem/BUILD.bazel b/pkg/proto/configuration/filesystem/BUILD.bazel index eda00b89..69e6725f 100644 --- a/pkg/proto/configuration/filesystem/BUILD.bazel +++ b/pkg/proto/configuration/filesystem/BUILD.bazel @@ -6,10 +6,7 @@ proto_library( name = "filesystem_proto", srcs = ["filesystem.proto"], visibility = ["//visibility:public"], - deps = [ - "@com_github_buildbarn_bb_storage//pkg/proto/configuration/blockdevice:blockdevice_proto", - "@protobuf//:empty_proto", - ], + deps = ["@com_github_buildbarn_bb_storage//pkg/proto/configuration/blockdevice:blockdevice_proto"], ) go_proto_library( diff --git a/pkg/proto/configuration/filesystem/filesystem.pb.go b/pkg/proto/configuration/filesystem/filesystem.pb.go index 70770b9c..6bd6aded 100644 --- a/pkg/proto/configuration/filesystem/filesystem.pb.go +++ b/pkg/proto/configuration/filesystem/filesystem.pb.go @@ -10,7 +10,6 @@ import ( blockdevice "github.com/buildbarn/bb-storage/pkg/proto/configuration/blockdevice" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - emptypb "google.golang.org/protobuf/types/known/emptypb" reflect "reflect" sync "sync" unsafe "unsafe" @@ -27,8 +26,6 @@ type FilePoolConfiguration struct { state protoimpl.MessageState `protogen:"open.v1"` // Types that are valid to be assigned to Backend: // - // *FilePoolConfiguration_InMemory - // *FilePoolConfiguration_DirectoryPath // *FilePoolConfiguration_BlockDevice Backend isFilePoolConfiguration_Backend `protobuf_oneof:"backend"` unknownFields protoimpl.UnknownFields @@ -72,24 +69,6 @@ func (x *FilePoolConfiguration) GetBackend() isFilePoolConfiguration_Backend { return nil } -func (x *FilePoolConfiguration) GetInMemory() *emptypb.Empty { - if x != nil { - if x, ok := x.Backend.(*FilePoolConfiguration_InMemory); ok { - return x.InMemory - } - } - return nil -} - -func (x *FilePoolConfiguration) GetDirectoryPath() string { - if x != nil { - if x, ok := x.Backend.(*FilePoolConfiguration_DirectoryPath); ok { - return x.DirectoryPath - } - } - return "" -} - func (x *FilePoolConfiguration) GetBlockDevice() *blockdevice.Configuration { if x != nil { if x, ok := x.Backend.(*FilePoolConfiguration_BlockDevice); ok { @@ -103,34 +82,20 @@ type isFilePoolConfiguration_Backend interface { isFilePoolConfiguration_Backend() } -type FilePoolConfiguration_InMemory struct { - InMemory *emptypb.Empty `protobuf:"bytes,1,opt,name=in_memory,json=inMemory,proto3,oneof"` -} - -type FilePoolConfiguration_DirectoryPath struct { - DirectoryPath string `protobuf:"bytes,2,opt,name=directory_path,json=directoryPath,proto3,oneof"` -} - type FilePoolConfiguration_BlockDevice struct { BlockDevice *blockdevice.Configuration `protobuf:"bytes,3,opt,name=block_device,json=blockDevice,proto3,oneof"` } -func (*FilePoolConfiguration_InMemory) isFilePoolConfiguration_Backend() {} - -func (*FilePoolConfiguration_DirectoryPath) isFilePoolConfiguration_Backend() {} - func (*FilePoolConfiguration_BlockDevice) isFilePoolConfiguration_Backend() {} var File_pkg_proto_configuration_filesystem_filesystem_proto protoreflect.FileDescriptor const file_pkg_proto_configuration_filesystem_filesystem_proto_rawDesc = "" + "\n" + - "3pkg/proto/configuration/filesystem/filesystem.proto\x12\"buildbarn.configuration.filesystem\x1a\x1bgoogle/protobuf/empty.proto\x1a5pkg/proto/configuration/blockdevice/blockdevice.proto\"\xdb\x01\n" + - "\x15FilePoolConfiguration\x125\n" + - "\tin_memory\x18\x01 \x01(\v2\x16.google.protobuf.EmptyH\x00R\binMemory\x12'\n" + - "\x0edirectory_path\x18\x02 \x01(\tH\x00R\rdirectoryPath\x12W\n" + + "3pkg/proto/configuration/filesystem/filesystem.proto\x12\"buildbarn.configuration.filesystem\x1a5pkg/proto/configuration/blockdevice/blockdevice.proto\"\x87\x01\n" + + "\x15FilePoolConfiguration\x12W\n" + "\fblock_device\x18\x03 \x01(\v22.buildbarn.configuration.blockdevice.ConfigurationH\x00R\vblockDeviceB\t\n" + - "\abackendBMZKgithub.com/buildbarn/bb-remote-execution/pkg/proto/configuration/filesystemb\x06proto3" + "\abackendJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03BMZKgithub.com/buildbarn/bb-remote-execution/pkg/proto/configuration/filesystemb\x06proto3" var ( file_pkg_proto_configuration_filesystem_filesystem_proto_rawDescOnce sync.Once @@ -147,17 +112,15 @@ func file_pkg_proto_configuration_filesystem_filesystem_proto_rawDescGZIP() []by var file_pkg_proto_configuration_filesystem_filesystem_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_pkg_proto_configuration_filesystem_filesystem_proto_goTypes = []any{ (*FilePoolConfiguration)(nil), // 0: buildbarn.configuration.filesystem.FilePoolConfiguration - (*emptypb.Empty)(nil), // 1: google.protobuf.Empty - (*blockdevice.Configuration)(nil), // 2: buildbarn.configuration.blockdevice.Configuration + (*blockdevice.Configuration)(nil), // 1: buildbarn.configuration.blockdevice.Configuration } var file_pkg_proto_configuration_filesystem_filesystem_proto_depIdxs = []int32{ - 1, // 0: buildbarn.configuration.filesystem.FilePoolConfiguration.in_memory:type_name -> google.protobuf.Empty - 2, // 1: buildbarn.configuration.filesystem.FilePoolConfiguration.block_device:type_name -> buildbarn.configuration.blockdevice.Configuration - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 1, // 0: buildbarn.configuration.filesystem.FilePoolConfiguration.block_device:type_name -> buildbarn.configuration.blockdevice.Configuration + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_pkg_proto_configuration_filesystem_filesystem_proto_init() } @@ -166,8 +129,6 @@ func file_pkg_proto_configuration_filesystem_filesystem_proto_init() { return } file_pkg_proto_configuration_filesystem_filesystem_proto_msgTypes[0].OneofWrappers = []any{ - (*FilePoolConfiguration_InMemory)(nil), - (*FilePoolConfiguration_DirectoryPath)(nil), (*FilePoolConfiguration_BlockDevice)(nil), } type x struct{} diff --git a/pkg/proto/configuration/filesystem/filesystem.proto b/pkg/proto/configuration/filesystem/filesystem.proto index e16c4534..3841970b 100644 --- a/pkg/proto/configuration/filesystem/filesystem.proto +++ b/pkg/proto/configuration/filesystem/filesystem.proto @@ -2,20 +2,23 @@ syntax = "proto3"; package buildbarn.configuration.filesystem; -import "google/protobuf/empty.proto"; import "pkg/proto/configuration/blockdevice/blockdevice.proto"; option go_package = "github.com/buildbarn/bb-remote-execution/pkg/proto/configuration/filesystem"; message FilePoolConfiguration { - oneof backend { - // Store all temporary files in memory. - google.protobuf.Empty in_memory = 1; + // Was 'in_memory'. This filepool configuration did not have support + // for sparse files and performed poorly when memory constrained. It + // has been removed. + reserved 1; - // Store all temporary files in a single directory on a file system. - // This option denotes the path of this directory. - string directory_path = 2; + // Was 'directory_path'. Write operations were implemented by opening + // the file, writing to it, and closing it. Reading was implemented + // similarly. This led to significant performance overhead. Use + // 'block_device' backed by a single file instead. + reserved 2; + oneof backend { // Store all temporary files in a single file on a file system or on // a raw block device. buildbarn.configuration.blockdevice.Configuration block_device = 3;