github.com/tiagovtristao/plz@v13.4.0+incompatible/src/cache/cache.go (about) 1 // Caching support for Please. 2 3 package cache 4 5 import ( 6 "github.com/thought-machine/please/src/core" 7 "net/http" 8 "sync" 9 10 "gopkg.in/op/go-logging.v1" 11 ) 12 13 var log = logging.MustGetLogger("cache") 14 15 // NewCache is the factory function for creating a cache setup from the given config. 16 func NewCache(config *core.Configuration) core.Cache { 17 c := newSyncCache(config, false) 18 if config.Cache.Workers > 0 { 19 return newAsyncCache(c, config) 20 } 21 return c 22 } 23 24 // newSyncCache creates a new cache, possibly multiplexing many underneath. 25 func newSyncCache(config *core.Configuration, remoteOnly bool) core.Cache { 26 mplex := &cacheMultiplexer{} 27 if config.Cache.Dir != "" && !remoteOnly { 28 mplex.caches = append(mplex.caches, newDirCache(config)) 29 } 30 if config.Cache.RPCURL != "" { 31 cache, err := newRPCCache(config) 32 if err == nil { 33 mplex.caches = append(mplex.caches, cache) 34 } else { 35 log.Warning("RPC cache server could not be reached: %s", err) 36 } 37 } 38 if config.Cache.HTTPURL != "" { 39 res, err := http.Get(config.Cache.HTTPURL.String() + "/ping") 40 if err == nil && res.StatusCode == 200 { 41 mplex.caches = append(mplex.caches, newHTTPCache(config)) 42 } else { 43 log.Warning("Http cache server could not be reached: %s.\nSkipping http caching...", err) 44 } 45 } 46 if len(mplex.caches) == 0 { 47 return nil 48 } else if len(mplex.caches) == 1 { 49 return mplex.caches[0] // Skip the extra layer of indirection 50 } 51 return mplex 52 } 53 54 // A cacheMultiplexer multiplexes several caches into one. 55 // Used when we have several active (eg. http, dir). 56 type cacheMultiplexer struct { 57 caches []core.Cache 58 } 59 60 func (mplex cacheMultiplexer) Store(target *core.BuildTarget, key []byte, files ...string) { 61 mplex.storeUntil(target, key, files, len(mplex.caches)) 62 } 63 64 // storeUntil stores artifacts into higher priority caches than the given one. 65 // Used after artifact retrieval to ensure we have them in eg. the directory cache after 66 // downloading from the RPC cache. 67 // This is a little inefficient since we could write the file to plz-out then copy it to the dir cache, 68 // but it's hard to fix that without breaking the cache abstraction. 69 func (mplex cacheMultiplexer) storeUntil(target *core.BuildTarget, key []byte, files []string, stopAt int) { 70 // Attempt to store on all caches simultaneously. 71 var wg sync.WaitGroup 72 for i, cache := range mplex.caches { 73 if i == stopAt { 74 break 75 } 76 wg.Add(1) 77 go func(cache core.Cache) { 78 cache.Store(target, key, files...) 79 wg.Done() 80 }(cache) 81 } 82 wg.Wait() 83 } 84 85 func (mplex cacheMultiplexer) StoreExtra(target *core.BuildTarget, key []byte, file string) { 86 mplex.storeExtraUntil(target, key, file, len(mplex.caches)) 87 } 88 89 // storeExtraUntil is similar to storeUntil but stores a single file. 90 func (mplex cacheMultiplexer) storeExtraUntil(target *core.BuildTarget, key []byte, file string, stopAt int) { 91 // Attempt to store on all caches simultaneously. 92 var wg sync.WaitGroup 93 for i, cache := range mplex.caches { 94 if i == stopAt { 95 break 96 } 97 wg.Add(1) 98 go func(cache core.Cache) { 99 cache.StoreExtra(target, key, file) 100 wg.Done() 101 }(cache) 102 } 103 wg.Wait() 104 } 105 106 func (mplex cacheMultiplexer) Retrieve(target *core.BuildTarget, key []byte) bool { 107 // Retrieve from caches sequentially; if we did them simultaneously we could 108 // easily write the same file from two goroutines at once. 109 for i, cache := range mplex.caches { 110 if cache.Retrieve(target, key) { 111 // Store this into other caches 112 mplex.storeUntil(target, key, nil, i) 113 return true 114 } 115 } 116 return false 117 } 118 119 func (mplex cacheMultiplexer) RetrieveExtra(target *core.BuildTarget, key []byte, file string) bool { 120 // Retrieve from caches sequentially; if we did them simultaneously we could 121 // easily write the same file from two goroutines at once. 122 for i, cache := range mplex.caches { 123 if cache.RetrieveExtra(target, key, file) { 124 // Store this into other caches 125 mplex.storeExtraUntil(target, key, file, i) 126 return true 127 } 128 } 129 return false 130 } 131 132 func (mplex cacheMultiplexer) Clean(target *core.BuildTarget) { 133 for _, cache := range mplex.caches { 134 cache.Clean(target) 135 } 136 } 137 138 func (mplex cacheMultiplexer) CleanAll() { 139 for _, cache := range mplex.caches { 140 cache.CleanAll() 141 } 142 } 143 144 func (mplex cacheMultiplexer) Shutdown() { 145 for _, cache := range mplex.caches { 146 cache.Shutdown() 147 } 148 } 149 150 // Returns all cacheable artifacts from this target. 151 func cacheArtifacts(target *core.BuildTarget, files ...string) []string { 152 return append(target.Outputs(), files...) 153 }