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  }