Skip to content
This repository has been archived by the owner on Sep 9, 2020. It is now read-only.

gps: source cache: adding close() methods #1024

Merged
merged 2 commits into from
Aug 29, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/dep/godep_importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ func TestGodepConfig_Import(t *testing.T) {
h.TempCopy(filepath.Join(testProjectRoot, godepPath), "godep/Godeps.json")

projectRoot := h.Path(testProjectRoot)
sm, err := gps.NewSourceManager(h.Path(cacheDir))
sm, err := gps.NewSourceManager(gps.SourceManagerConfig{
Cachedir: h.Path(cacheDir),
Logger: log.New(test.Writer{t}, "", 0),
})
h.Must(err)
defer sm.Release()

Expand Down
2 changes: 1 addition & 1 deletion cmd/dep/govend_importer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestGovendConfig_Import(t *testing.T) {
h.TempCopy(filepath.Join(testProjectRoot, govendYAMLName), "govend/vendor.yml")

projectRoot := h.Path(testProjectRoot)
sm, err := gps.NewSourceManager(h.Path(cacheDir))
sm, err := gps.NewSourceManager(gps.SourceManagerConfig{Cachedir: h.Path(cacheDir)})
h.Must(err)
defer sm.Release()

Expand Down
5 changes: 4 additions & 1 deletion context.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ func defaultGOPATH() string {
}

func (c *Ctx) SourceManager() (*gps.SourceMgr, error) {
return gps.NewSourceManager(filepath.Join(c.GOPATH, "pkg", "dep"))
return gps.NewSourceManager(gps.SourceManagerConfig{
Cachedir: filepath.Join(c.GOPATH, "pkg", "dep"),
Logger: c.Out,
})
}

// LoadProject starts from the current working directory and searches up the
Expand Down
2 changes: 1 addition & 1 deletion internal/gps/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func main() {

// Set up a SourceManager. This manages interaction with sources (repositories).
tempdir, _ := ioutil.TempDir("", "gps-repocache")
sourcemgr, _ := gps.NewSourceManager(filepath.Join(tempdir))
sourcemgr, _ := gps.NewSourceManager(gps.SourceManagerConfig{Cachedir: filepath.Join(tempdir)})
defer sourcemgr.Release()

// Prep and run the solver
Expand Down
29 changes: 23 additions & 6 deletions internal/gps/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"path"
"path/filepath"
Expand All @@ -16,6 +17,8 @@ import (
"sync/atomic"
"testing"
"time"

"github.com/golang/dep/internal/test"
)

// An analyzer that passes nothing back, but doesn't error. This is the naive
Expand All @@ -40,7 +43,10 @@ func mkNaiveSM(t *testing.T) (*SourceMgr, func()) {
t.Fatalf("Failed to create temp dir: %s", err)
}

sm, err := NewSourceManager(cpath)
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: cpath,
Logger: log.New(test.Writer{t}, "", 0),
})
if err != nil {
t.Fatalf("Unexpected error on SourceManager creation: %s", err)
}
Expand All @@ -58,7 +64,10 @@ func remakeNaiveSM(osm *SourceMgr, t *testing.T) (*SourceMgr, func()) {
cpath := osm.cachedir
osm.Release()

sm, err := NewSourceManager(cpath)
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: cpath,
Logger: log.New(test.Writer{t}, "", 0),
})
if err != nil {
t.Fatalf("unexpected error on SourceManager recreation: %s", err)
}
Expand All @@ -77,13 +86,18 @@ func TestSourceManagerInit(t *testing.T) {
if err != nil {
t.Errorf("Failed to create temp dir: %s", err)
}
sm, err := NewSourceManager(cpath)
cfg := SourceManagerConfig{
Cachedir: cpath,
Logger: log.New(test.Writer{t}, "", 0),
}

sm, err := NewSourceManager(cfg)

if err != nil {
t.Errorf("Unexpected error on SourceManager creation: %s", err)
}

_, err = NewSourceManager(cpath)
_, err = NewSourceManager(cfg)
if err == nil {
t.Errorf("Creating second SourceManager should have failed due to file lock contention")
} else if te, ok := err.(CouldNotCreateLockError); !ok {
Expand All @@ -105,7 +119,7 @@ func TestSourceManagerInit(t *testing.T) {
}

// Set another one up at the same spot now, just to be sure
sm, err = NewSourceManager(cpath)
sm, err = NewSourceManager(cfg)
if err != nil {
t.Errorf("Creating a second SourceManager should have succeeded when the first was released, but failed with err %s", err)
}
Expand All @@ -128,7 +142,10 @@ func TestSourceInit(t *testing.T) {
t.Fatalf("Failed to create temp dir: %s", err)
}

sm, err := NewSourceManager(cpath)
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: cpath,
Logger: log.New(test.Writer{t}, "", 0),
})
if err != nil {
t.Fatalf("Unexpected error on SourceManager creation: %s", err)
}
Expand Down
9 changes: 7 additions & 2 deletions internal/gps/result_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"path"
"path/filepath"
"testing"

"github.com/golang/dep/internal/test"
)

var discardLogger = log.New(ioutil.Discard, "", 0)
Expand Down Expand Up @@ -122,9 +124,12 @@ func BenchmarkCreateVendorTree(b *testing.B) {
tmp := path.Join(os.TempDir(), "vsolvtest")

clean := true
sm, err := NewSourceManager(path.Join(tmp, "cache"))
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: path.Join(tmp, "cache"),
Logger: log.New(test.Writer{b}, "", 0),
})
if err != nil {
b.Errorf("NewSourceManager errored unexpectedly: %q", err)
b.Errorf("failed to create SourceManager: %q", err)
clean = false
}

Expand Down
25 changes: 3 additions & 22 deletions internal/gps/solve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ import (
"log"
"reflect"
"sort"
"strings"
"testing"
"unicode"

"github.com/golang/dep/internal/test"
)

// overrideMkBridge overrides the base bridge with the depspecBridge that skips
Expand All @@ -21,30 +21,11 @@ func overrideMkBridge(s *solver, sm SourceManager, down bool) sourceBridge {
return &depspecBridge{mkBridge(s, sm, down)}
}

type testlogger struct {
*testing.T
}

func (t testlogger) Write(b []byte) (n int, err error) {
str := string(b)
if len(str) == 0 {
return 0, nil
}

for _, part := range strings.Split(str, "\n") {
str := strings.TrimRightFunc(part, unicode.IsSpace)
if len(str) != 0 {
t.T.Log(str)
}
}
return len(b), err
}

func fixSolve(params SolveParameters, sm SourceManager, t *testing.T) (Solution, error) {
// Trace unconditionally; by passing the trace through t.Log(), the testing
// system will decide whether or not to actually show the output (based on
// -v, or selectively on test failure).
params.TraceLogger = log.New(testlogger{T: t}, "", 0)
params.TraceLogger = log.New(test.Writer{t}, "", 0)
// always return false, otherwise it would identify pretty much all of
// our fixtures as being stdlib and skip everything
params.stdLibFn = func(string) bool { return false }
Expand Down
5 changes: 4 additions & 1 deletion internal/gps/solver_inputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ func TestValidateParams(t *testing.T) {

cacheDir := "gps-cache"
h.TempDir(cacheDir)
sm, err := NewSourceManager(h.Path(cacheDir))
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: h.Path(cacheDir),
Logger: log.New(test.Writer{t}, "", 0),
})
h.Must(err)
defer sm.Release()

Expand Down
19 changes: 17 additions & 2 deletions internal/gps/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ package gps

import (
"context"
"errors"
"fmt"
"log"
"sync"

"github.com/golang/dep/internal/gps/pkgtree"
"github.com/pkg/errors"
)

// sourceState represent the states that a source can be in, depending on how
Expand Down Expand Up @@ -49,19 +50,29 @@ type sourceCoordinator struct {
protoSrcs map[string][]srcReturnChans
deducer deducer
cachedir string
logger *log.Logger
}

func newSourceCoordinator(superv *supervisor, deducer deducer, cachedir string) *sourceCoordinator {
func newSourceCoordinator(superv *supervisor, deducer deducer, cachedir string, logger *log.Logger) *sourceCoordinator {
return &sourceCoordinator{
supervisor: superv,
deducer: deducer,
cachedir: cachedir,
logger: logger,
srcs: make(map[string]*sourceGateway),
nameToURL: make(map[string]string),
protoSrcs: make(map[string][]srcReturnChans),
}
}

func (sc *sourceCoordinator) close() {
for k, v := range sc.srcs {
if err := v.close(); err != nil {
sc.logger.Println(errors.Wrapf(err, "error closing source gateway for %q", k))
}
}
}

func (sc *sourceCoordinator) getSourceGatewayFor(ctx context.Context, id ProjectIdentifier) (*sourceGateway, error) {
if sc.supervisor.getLifetimeContext().Err() != nil {
return nil, errors.New("sourceCoordinator has been terminated")
Expand Down Expand Up @@ -200,6 +211,10 @@ func newSourceGateway(maybe maybeSource, superv *supervisor, cachedir string) *s
return sg
}

func (sg *sourceGateway) close() error {
return errors.Wrap(sg.cache.close(), "error closing cache")
}

func (sg *sourceGateway) syncLocal(ctx context.Context) error {
sg.mu.Lock()
defer sg.mu.Unlock()
Expand Down
5 changes: 5 additions & 0 deletions internal/gps/source_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ type singleSourceCache interface {
// If the input is a revision and multiple UnpairedVersions are associated
// with it, whatever happens to be the first is returned.
toUnpaired(v Version) (UnpairedVersion, bool)

// close closes the cache and any backing resources.
close() error
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Embed an io.Closer in the interface instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the doc is so open ended for this particular interface, I'm not sure that it buys us much. I also hesitate to export a method before it needs to be. I don't really have strong feelings either way though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither do I. Just a suggestion. 😁

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah let's please keep this unexported and not worry aboutio.Closer()

}

type singleSourceCacheMemory struct {
Expand All @@ -77,6 +80,8 @@ func newMemoryCache() singleSourceCache {
}
}

func (*singleSourceCacheMemory) close() error { return nil }

type projectInfo struct {
Manifest
Lock
Expand Down
32 changes: 22 additions & 10 deletions internal/gps/source_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ package gps
import (
"context"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
Expand Down Expand Up @@ -43,7 +45,6 @@ type SourceManager interface {

// ListVersions retrieves a list of the available versions for a given
// repository name.
// TODO convert to []PairedVersion
ListVersions(ProjectIdentifier) ([]PairedVersion, error)

// RevisionPresentIn indicates whether the provided Version is present in
Expand Down Expand Up @@ -133,9 +134,13 @@ func (smIsReleased) Error() string {

var _ SourceManager = &SourceMgr{}

// NewSourceManager produces an instance of gps's built-in SourceManager. It
// takes a cache directory, where local instances of upstream sources are
// stored.
// SourceManagerConfig holds configuration information for creating SourceMgrs.
type SourceManagerConfig struct {
Cachedir string // Where to store local instances of upstream sources.
Logger *log.Logger // Optional info/warn logger. Discards if nil.
}

// NewSourceManager produces an instance of gps's built-in SourceManager.
//
// The returned SourceManager aggressively caches information wherever possible.
// If tools need to do preliminary work involving upstream repository analysis
Expand All @@ -146,8 +151,12 @@ var _ SourceManager = &SourceMgr{}
// gps's SourceManager is intended to be threadsafe (if it's not, please file a
// bug!). It should be safe to reuse across concurrent solving runs, even on
// unrelated projects.
func NewSourceManager(cachedir string) (*SourceMgr, error) {
err := os.MkdirAll(filepath.Join(cachedir, "sources"), 0777)
func NewSourceManager(c SourceManagerConfig) (*SourceMgr, error) {
if c.Logger == nil {
c.Logger = log.New(ioutil.Discard, "", 0)
}

err := os.MkdirAll(filepath.Join(c.Cachedir, "sources"), 0777)
if err != nil {
return nil, err
}
Expand All @@ -159,7 +168,7 @@ func NewSourceManager(cachedir string) (*SourceMgr, error) {
// a process keeping the lock busy, it will pass back a temporary error that
// we can spin on.

glpath := filepath.Join(cachedir, "sm.lock")
glpath := filepath.Join(c.Cachedir, "sm.lock")
lockfile, err := lockfile.New(glpath)
if err != nil {
return nil, CouldNotCreateLockError{
Expand Down Expand Up @@ -222,12 +231,12 @@ func NewSourceManager(cachedir string) (*SourceMgr, error) {
deducer := newDeductionCoordinator(superv)

sm := &SourceMgr{
cachedir: cachedir,
cachedir: c.Cachedir,
lf: &lockfile,
suprvsr: superv,
cancelAll: cf,
deduceCoord: deducer,
srcCoord: newSourceCoordinator(superv, deducer, cachedir),
srcCoord: newSourceCoordinator(superv, deducer, c.Cachedir, c.Logger),
qch: make(chan struct{}),
}

Expand Down Expand Up @@ -348,7 +357,7 @@ func (sm *SourceMgr) Release() {
// until after the doRelease process is done, as doing so could cause the
// process to terminate before a signal-driven doRelease() call has a chance
// to finish its cleanup.
sm.relonce.Do(func() { sm.doRelease() })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hah 😄

sm.relonce.Do(sm.doRelease)
}

// doRelease actually releases physical resources (files on disk, etc.).
Expand All @@ -360,6 +369,9 @@ func (sm *SourceMgr) doRelease() {
sm.cancelAll()
sm.suprvsr.wait()

// Close the source coordinator.
sm.srcCoord.close()

// Close the file handle for the lock file and remove it from disk
sm.lf.Unlock()
os.Remove(filepath.Join(sm.cachedir, "sm.lock"))
Expand Down
6 changes: 5 additions & 1 deletion internal/gps/source_manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package gps

import (
"log"
"reflect"
"testing"

Expand Down Expand Up @@ -87,7 +88,10 @@ func TestSourceManager_InferConstraint(t *testing.T) {
cacheDir := "gps-repocache"
h.TempDir(cacheDir)

sm, err := NewSourceManager(h.Path(cacheDir))
sm, err := NewSourceManager(SourceManagerConfig{
Cachedir: h.Path(cacheDir),
Logger: log.New(test.Writer{t}, "", 0),
})
h.Must(err)

got, err := sm.InferConstraint(tc.str, tc.project)
Expand Down
Loading