/
cache.go
103 lines (90 loc) · 2.92 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Caching support for Please.
package cache
import (
"sync"
"github.com/thought-machine/please/src/cli/logging"
"github.com/thought-machine/please/src/core"
)
var log = logging.Log
// NewCache is the factory function for creating a cache setup from the given config.
func NewCache(state *core.BuildState) core.Cache {
c := newSyncCache(state, false)
if state.Config.Cache.Workers > 0 {
return newAsyncCache(c, state.Config)
}
return c
}
// newSyncCache creates a new cache, possibly multiplexing many underneath.
func newSyncCache(state *core.BuildState, remoteOnly bool) core.Cache {
mplex := &cacheMultiplexer{}
if state.Config.Cache.Dir != "" && !remoteOnly {
mplex.caches = append(mplex.caches, newDirCache(state.Config))
}
if state.Config.Cache.HTTPURL != "" {
mplex.caches = append(mplex.caches, newHTTPCache(state.Config))
}
if state.Config.Cache.RetrieveCommand != "" {
mplex.caches = append(mplex.caches, newCmdCache(state.Config))
}
if len(mplex.caches) == 0 {
return &noopCache{}
} else if len(mplex.caches) == 1 {
return mplex.caches[0] // Skip the extra layer of indirection
}
return mplex
}
// A cacheMultiplexer multiplexes several caches into one.
// Used when we have several active (eg. http, dir).
type cacheMultiplexer struct {
caches []core.Cache
}
func (mplex cacheMultiplexer) Store(target *core.BuildTarget, key []byte, files []string) {
mplex.storeUntil(target, key, files, len(mplex.caches))
}
// storeUntil stores artifacts into higher priority caches than the given one.
// Used after artifact retrieval to ensure we have them in eg. the directory cache after
// downloading from the RPC cache.
// This is a little inefficient since we could write the file to plz-out then copy it to the dir cache,
// but it's hard to fix that without breaking the cache abstraction.
func (mplex cacheMultiplexer) storeUntil(target *core.BuildTarget, key []byte, files []string, stopAt int) {
// Attempt to store on all caches simultaneously.
var wg sync.WaitGroup
for i, cache := range mplex.caches {
if i == stopAt {
break
}
wg.Add(1)
go func(cache core.Cache) {
cache.Store(target, key, files)
wg.Done()
}(cache)
}
wg.Wait()
}
func (mplex cacheMultiplexer) Retrieve(target *core.BuildTarget, key []byte, files []string) bool {
// Retrieve from caches sequentially; if we did them simultaneously we could
// easily write the same file from two goroutines at once.
for i, cache := range mplex.caches {
if ok := cache.Retrieve(target, key, files); ok {
// Store this into other caches
mplex.storeUntil(target, key, files, i)
return ok
}
}
return false
}
func (mplex cacheMultiplexer) Clean(target *core.BuildTarget) {
for _, cache := range mplex.caches {
cache.Clean(target)
}
}
func (mplex cacheMultiplexer) CleanAll() {
for _, cache := range mplex.caches {
cache.CleanAll()
}
}
func (mplex cacheMultiplexer) Shutdown() {
for _, cache := range mplex.caches {
cache.Shutdown()
}
}