ref: ab6f8dde0cdc554084c4455c76feef0099db70d9
parent: 1ac6099100ed61a1e49766f190deae8b426a1ea6
author: Runxi Yu <runxiyu@umich.edu>
date: Sun Mar 22 19:10:37 EDT 2026
*: Fixup ownership of compositional backends
--- a/objectstore/chain/bytes.go
+++ b/objectstore/chain/bytes.go
@@ -12,10 +12,6 @@
// ReadBytesFull reads a full serialized object from the first backend that has it.
func (chain *Chain) ReadBytesFull(id objectid.ObjectID) ([]byte, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
full, err := backend.ReadBytesFull(id)
if err == nil {return full, nil
@@ -34,10 +30,6 @@
// ReadBytesContent reads an object's type and content bytes from the first backend that has it.
func (chain *Chain) ReadBytesContent(id objectid.ObjectID) (objecttype.Type, []byte, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
ty, content, err := backend.ReadBytesContent(id)
if err == nil {return ty, content, nil
--- a/objectstore/chain/chain.go
+++ b/objectstore/chain/chain.go
@@ -1,4 +1,5 @@
-// Package chain provides a wrapper object storage backend to query a chain of backends.
+// Package chain provides a wrapper object storage backend to query a chain of
+// backends.
package chain
import (
@@ -6,6 +7,8 @@
)
// Chain queries multiple object databases in order.
+//
+// Chain borrows its backend stores.
type Chain struct {backends []objectstore.Store
}
--- a/objectstore/chain/close.go
+++ b/objectstore/chain/close.go
@@ -1,21 +1,8 @@
package chain
-import "errors"
-
-// Close closes all backends and joins close errors.
-func (chain *Chain) Close() error {- var errs []error
-
- for _, backend := range chain.backends {- if backend == nil {- continue
- }
-
- err := backend.Close()
- if err != nil {- errs = append(errs, err)
- }
- }
-
- return errors.Join(errs...)
-}
+// Close releases wrapper-local resources.
+//
+// Chain borrows its backends, so Close does not close them.
+//
+// Repeated calls to Close are undefined behavior.
+func (chain *Chain) Close() error { return nil }--- a/objectstore/chain/header.go
+++ b/objectstore/chain/header.go
@@ -12,10 +12,6 @@
// ReadHeader reads object header data from the first backend that has it.
func (chain *Chain) ReadHeader(id objectid.ObjectID) (objecttype.Type, int64, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
ty, size, err := backend.ReadHeader(id)
if err == nil {return ty, size, nil
--- a/objectstore/chain/new.go
+++ b/objectstore/chain/new.go
@@ -3,6 +3,9 @@
import "codeberg.org/lindenii/furgit/objectstore"
// New creates an ordered object database chain.
+//
+// The provided backends must be non-nil and distinct.
+// Chain borrows the provided backends and does not close them in Close.
func New(backends ...objectstore.Store) *Chain { return &Chain{backends: append([]objectstore.Store(nil), backends...),
--- a/objectstore/chain/reader.go
+++ b/objectstore/chain/reader.go
@@ -13,10 +13,6 @@
// ReadReaderFull reads a full serialized object stream from the first backend that has it.
func (chain *Chain) ReadReaderFull(id objectid.ObjectID) (io.ReadCloser, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
reader, err := backend.ReadReaderFull(id)
if err == nil {return reader, nil
@@ -35,10 +31,6 @@
// ReadReaderContent reads an object's type, declared content length, and content stream from the first backend that has it.
func (chain *Chain) ReadReaderContent(id objectid.ObjectID) (objecttype.Type, int64, io.ReadCloser, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
ty, size, reader, err := backend.ReadReaderContent(id)
if err == nil {return ty, size, reader, nil
--- a/objectstore/chain/refresh.go
+++ b/objectstore/chain/refresh.go
@@ -7,10 +7,6 @@
var errs []error
for _, backend := range chain.backends {- if backend == nil {- continue
- }
-
err := backend.Refresh()
if err != nil {errs = append(errs, err)
--- a/objectstore/chain/size.go
+++ b/objectstore/chain/size.go
@@ -11,10 +11,6 @@
// ReadSize reads object content length from the first backend that has it.
func (chain *Chain) ReadSize(id objectid.ObjectID) (int64, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
size, err := backend.ReadSize(id)
if err == nil {return size, nil
--- a/objectstore/mix/close.go
+++ b/objectstore/mix/close.go
@@ -1,30 +1,8 @@
package mix
-import (
- "errors"
-
- "codeberg.org/lindenii/furgit/objectstore"
-)
-
-// Close closes all backends and joins close errors.
-func (mix *Mix) Close() error {- mix.mu.RLock()
-
- backends := make([]objectstore.Store, 0, len(mix.backendNodeByStore))
- for node := mix.backendHead; node != nil; node = node.next {- backends = append(backends, node.backend)
- }
-
- mix.mu.RUnlock()
-
- var errs []error
-
- for _, backend := range backends {- err := backend.Close()
- if err != nil {- errs = append(errs, err)
- }
- }
-
- return errors.Join(errs...)
-}
+// Close releases wrapper-local resources.
+//
+// Mix borrows its backends, so Close does not close them.
+//
+// Repeated calls to Close are undefined behavior.
+func (mix *Mix) Close() error { return nil }--- a/objectstore/mix/mix.go
+++ b/objectstore/mix/mix.go
@@ -9,6 +9,8 @@
)
// Mix queries multiple object databases with an MRU backend preference.
+//
+// Mix borrows its backend stores.
type Mix struct {mu sync.RWMutex
--- a/objectstore/mix/new.go
+++ b/objectstore/mix/new.go
@@ -3,6 +3,9 @@
import "codeberg.org/lindenii/furgit/objectstore"
// New creates a Mix from backends.
+//
+// The provided backends must be non-nil and distinct.
+// Mix borrows the provided backends and does not close them in Close.
func New(backends ...objectstore.Store) *Mix {nodeByStore := make(map[objectstore.Store]*backendNode, len(backends))
@@ -12,10 +15,6 @@
)
for _, backend := range backends {- if backend == nil {- continue
- }
-
node := &backendNode{backend: backend,
prev: tail,
--- a/receivepack/service/run_hook.go
+++ b/receivepack/service/run_hook.go
@@ -41,6 +41,8 @@
var (
quarantineObjectsStore objectstore.Store
+ quarantineLooseStore *loose.Store
+ quarantinePackedStore *packed.Store
quarantineLooseRoot *os.Root
quarantinePackRoot *os.Root
err error
@@ -54,7 +56,7 @@
return nil, nil, nil, false, err.Error()
}
- quarantineLooseStore, err := loose.New(quarantineLooseRoot, service.opts.Algorithm)
+ quarantineLooseStore, err = loose.New(quarantineLooseRoot, service.opts.Algorithm)
if err != nil {_ = quarantineLooseRoot.Close()
@@ -68,7 +70,9 @@
quarantinePackRoot, err = quarantineLooseRoot.OpenRoot("pack") if err == nil {- quarantinePackedStore, packedErr := packed.New(quarantinePackRoot, service.opts.Algorithm, packed.Options{})+ var packedErr error
+
+ quarantinePackedStore, packedErr = packed.New(quarantinePackRoot, service.opts.Algorithm, packed.Options{}) if packedErr != nil {_ = quarantineLooseStore.Close()
_ = quarantinePackRoot.Close()
@@ -93,6 +97,14 @@
defer func() { if quarantineObjectsStore != nil {_ = quarantineObjectsStore.Close()
+ }
+
+ if quarantinePackedStore != nil {+ _ = quarantinePackedStore.Close()
+ }
+
+ if quarantineLooseStore != nil {+ _ = quarantineLooseStore.Close()
}
if quarantinePackRoot != nil {--- a/refstore/chain/chain.go
+++ b/refstore/chain/chain.go
@@ -5,6 +5,8 @@
import "codeberg.org/lindenii/furgit/refstore"
// Chain queries multiple reference stores in order.
+//
+// Chain borrows its backend stores.
type Chain struct {backends []refstore.ReadingStore
}
--- a/refstore/chain/close.go
+++ b/refstore/chain/close.go
@@ -1,21 +1,8 @@
package chain
-import "errors"
-
-// Close closes all backends and joins close errors.
-func (chain *Chain) Close() error {- var errs []error
-
- for _, backend := range chain.backends {- if backend == nil {- continue
- }
-
- err := backend.Close()
- if err != nil {- errs = append(errs, err)
- }
- }
-
- return errors.Join(errs...)
-}
+// Close releases wrapper-local resources.
+//
+// Chain borrows its backends, so Close does not close them.
+//
+// Repeated calls to Close are undefined behavior.
+func (chain *Chain) Close() error { return nil }--- a/refstore/chain/list.go
+++ b/refstore/chain/list.go
@@ -15,10 +15,6 @@
seen := map[string]struct{}{} for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
listed, err := backend.List(pattern)
if err != nil { return nil, fmt.Errorf("refstore: backend %d list: %w", i, err)--- a/refstore/chain/new.go
+++ b/refstore/chain/new.go
@@ -3,6 +3,9 @@
import "codeberg.org/lindenii/furgit/refstore"
// New creates an ordered reference store chain.
+//
+// The provided backends must be non-nil and distinct.
+// Chain borrows the provided backends and does not close them in Close.
func New(backends ...refstore.ReadingStore) *Chain { return &Chain{backends: append([]refstore.ReadingStore(nil), backends...),
--- a/refstore/chain/resolve.go
+++ b/refstore/chain/resolve.go
@@ -13,10 +13,6 @@
//nolint:ireturn
func (chain *Chain) Resolve(name string) (ref.Ref, error) { for i, backend := range chain.backends {- if backend == nil {- continue
- }
-
resolved, err := backend.Resolve(name)
if err == nil {return resolved, nil
--- a/repository/close.go
+++ b/repository/close.go
@@ -21,15 +21,15 @@
}
}
- if repo.objectsLooseForWritingOnly != nil {- err := repo.objectsLooseForWritingOnly.Close()
+ if repo.objectsPacked != nil {+ err := repo.objectsPacked.Close()
if err != nil {errs = append(errs, err)
}
}
- if repo.objectsWriteRoot != nil {- err := repo.objectsWriteRoot.Close()
+ if repo.objectsLoose != nil {+ err := repo.objectsLoose.Close()
if err != nil {errs = append(errs, err)
}
--- a/repository/objects.go
+++ b/repository/objects.go
@@ -20,8 +20,8 @@
objects objectstore.Store,
objectsRoot *os.Root,
objectsPackRoot *os.Root,
- objectsLooseForWritingOnly *objectloose.Store,
- objectsWriteRoot *os.Root,
+ objectsLoose *objectloose.Store,
+ objectsPacked *objectpacked.Store,
err error,
) { objectsRoot, err = root.OpenRoot("objects")@@ -29,7 +29,7 @@
return nil, nil, nil, nil, nil, fmt.Errorf("repository: open objects: %w", err)}
- looseStore, err := objectloose.New(objectsRoot, algo)
+ objectsLoose, err = objectloose.New(objectsRoot, algo)
if err != nil {_ = objectsRoot.Close()
@@ -36,13 +36,11 @@
return nil, nil, nil, nil, nil, err
}
- backends := []objectstore.Store{looseStore}+ backends := []objectstore.Store{objectsLoose} objectsPackRoot, err = objectsRoot.OpenRoot("pack") if err == nil {- var packedStore *objectpacked.Store
-
- packedStore, err = objectpacked.New(
+ objectsPacked, err = objectpacked.New(
objectsPackRoot,
algo,
objectpacked.Options{RefreshPolicy: objectpacked.RefreshPolicyNever},@@ -49,15 +47,15 @@
)
if err != nil {_ = objectsPackRoot.Close()
- _ = looseStore.Close()
+ _ = objectsLoose.Close()
_ = objectsRoot.Close()
return nil, nil, nil, nil, nil, err
}
- backends = append(backends, packedStore)
+ backends = append(backends, objectsPacked)
} else if !errors.Is(err, os.ErrNotExist) {- _ = looseStore.Close()
+ _ = objectsLoose.Close()
_ = objectsRoot.Close()
return nil, nil, nil, nil, nil, fmt.Errorf("repository: open objects/pack: %w", err)@@ -65,33 +63,7 @@
objects = objectmix.New(backends...)
- objectsWriteRoot, err = root.OpenRoot("objects")- if err != nil {- _ = objects.Close()
- if objectsPackRoot != nil {- _ = objectsPackRoot.Close()
- }
-
- _ = objectsRoot.Close()
-
- return nil, nil, nil, nil, nil, fmt.Errorf("repository: open objects for loose writing: %w", err)- }
-
- objectsLooseForWritingOnly, err = objectloose.New(objectsWriteRoot, algo)
- if err != nil {- _ = objects.Close()
-
- _ = objectsWriteRoot.Close()
- if objectsPackRoot != nil {- _ = objectsPackRoot.Close()
- }
-
- _ = objectsRoot.Close()
-
- return nil, nil, nil, nil, nil, err
- }
-
- return objects, objectsRoot, objectsPackRoot, objectsLooseForWritingOnly, objectsWriteRoot, nil
+ return objects, objectsRoot, objectsPackRoot, objectsLoose, objectsPacked, nil
}
// Objects returns the configured object store.
--- a/repository/open.go
+++ b/repository/open.go
@@ -33,7 +33,7 @@
repo.algo = algo
- objects, objectsRoot, objectsPackRoot, objectsLooseForWritingOnly, objectsWriteRoot, err := openObjectStore(root, algo)
+ objects, objectsRoot, objectsPackRoot, objectsLoose, objectsPacked, err := openObjectStore(root, algo)
if err != nil {return nil, err
}
@@ -41,8 +41,8 @@
repo.objects = objects
repo.objectsRoot = objectsRoot
repo.objectsPackRoot = objectsPackRoot
- repo.objectsLooseForWritingOnly = objectsLooseForWritingOnly
- repo.objectsWriteRoot = objectsWriteRoot
+ repo.objectsLoose = objectsLoose
+ repo.objectsPacked = objectsPacked
refRoot, err := root.OpenRoot(".") if err != nil {--- a/repository/repository.go
+++ b/repository/repository.go
@@ -8,6 +8,7 @@
"codeberg.org/lindenii/furgit/objectid"
"codeberg.org/lindenii/furgit/objectstore"
objectloose "codeberg.org/lindenii/furgit/objectstore/loose"
+ objectpacked "codeberg.org/lindenii/furgit/objectstore/packed"
"codeberg.org/lindenii/furgit/refstore"
)
@@ -19,11 +20,11 @@
config *config.Config
algo objectid.Algorithm
- objects objectstore.Store
- objectsRoot *os.Root
- objectsPackRoot *os.Root
- objectsLooseForWritingOnly *objectloose.Store
- objectsWriteRoot *os.Root
- refRoot *os.Root
- refs refstore.ReadWriteStore
+ objects objectstore.Store
+ objectsRoot *os.Root
+ objectsPackRoot *os.Root
+ objectsLoose *objectloose.Store
+ objectsPacked *objectpacked.Store
+ refRoot *os.Root
+ refs refstore.ReadWriteStore
}
--- a/repository/write_loose.go
+++ b/repository/write_loose.go
@@ -5,5 +5,5 @@
)
func (repo *Repository) LooseStoreForWriting() *objectloose.Store {- return repo.objectsLooseForWritingOnly
+ return repo.objectsLoose
}
--
⑨