package solver import ( "context" "strings" "sync" digest "github.com/opencontainers/go-digest" "github.com/pkg/errors" "golang.org/x/sync/errgroup" ) func newCombinedCacheManager(cms []CacheManager, main CacheManager) CacheManager { return &combinedCacheManager{cms: cms, main: main} } type combinedCacheManager struct { cms []CacheManager main CacheManager id string idOnce sync.Once } func (cm *combinedCacheManager) ID() string { cm.idOnce.Do(func() { ids := make([]string, len(cm.cms)) for i, c := range cm.cms { ids[i] = c.ID() } cm.id = digest.FromBytes([]byte(strings.Join(ids, ","))).String() }) return cm.id } func (cm *combinedCacheManager) Query(inp []CacheKeyWithSelector, inputIndex Index, dgst digest.Digest, outputIndex Index) ([]*CacheKey, error) { eg, _ := errgroup.WithContext(context.TODO()) keys := make(map[string]*CacheKey, len(cm.cms)) var mu sync.Mutex for _, c := range cm.cms { func(c CacheManager) { eg.Go(func() error { recs, err := c.Query(inp, inputIndex, dgst, outputIndex) if err != nil { return err } mu.Lock() for _, r := range recs { if _, ok := keys[r.ID]; !ok || c == cm.main { keys[r.ID] = r } } mu.Unlock() return nil }) }(c) } if err := eg.Wait(); err != nil { return nil, err } out := make([]*CacheKey, 0, len(keys)) for _, k := range keys { out = append(out, k) } return out, nil } func (cm *combinedCacheManager) Load(ctx context.Context, rec *CacheRecord) (Result, error) { res, err := rec.cacheManager.Load(ctx, rec) if err != nil { return nil, err } if _, err := cm.main.Save(rec.key, res); err != nil { return nil, err } return res, nil } func (cm *combinedCacheManager) Save(key *CacheKey, s Result) (*ExportableCacheKey, error) { return cm.main.Save(key, s) } func (cm *combinedCacheManager) Records(ck *CacheKey) ([]*CacheRecord, error) { if len(ck.ids) == 0 { return nil, errors.Errorf("no results") } records := map[string]*CacheRecord{} var mu sync.Mutex eg, _ := errgroup.WithContext(context.TODO()) for c := range ck.ids { func(c *cacheManager) { eg.Go(func() error { recs, err := c.Records(ck) if err != nil { return err } mu.Lock() for _, rec := range recs { if _, ok := records[rec.ID]; !ok || c == cm.main { if c == cm.main { rec.Priority = 1 } records[rec.ID] = rec } } mu.Unlock() return nil }) }(c) } if err := eg.Wait(); err != nil { return nil, err } out := make([]*CacheRecord, 0, len(records)) for _, rec := range records { out = append(out, rec) } return out, nil }