github.com/uber/kraken@v0.1.4/tracker/originstore/store.go (about)

     1  // Copyright (c) 2016-2019 Uber Technologies, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package originstore
    15  
    16  import (
    17  	"fmt"
    18  	"time"
    19  
    20  	"github.com/uber/kraken/core"
    21  	"github.com/uber/kraken/lib/hostlist"
    22  	"github.com/uber/kraken/origin/blobclient"
    23  	"github.com/uber/kraken/utils/dedup"
    24  	"github.com/uber/kraken/utils/errutil"
    25  	"github.com/uber/kraken/utils/log"
    26  
    27  	"github.com/andres-erbsen/clock"
    28  )
    29  
    30  type allUnavailableError struct {
    31  	error
    32  }
    33  
    34  // Store is a local cache in front of the origin cluster which is resilient to
    35  // origin unavailability.
    36  type Store interface {
    37  	// GetOrigins returns all available origins seeding d. Returns error if all origins
    38  	// are unavailable.
    39  	GetOrigins(d core.Digest) ([]*core.PeerInfo, error)
    40  }
    41  
    42  type store struct {
    43  	config       Config
    44  	origins      hostlist.List
    45  	provider     blobclient.Provider
    46  	locations    *dedup.Limiter // Caches results for origin locations per digest.
    47  	peerContexts *dedup.Limiter // Caches results for individual origin peer contexts.
    48  }
    49  
    50  // New creates a new Store.
    51  func New(config Config, clk clock.Clock, origins hostlist.List, provider blobclient.Provider) Store {
    52  	config.applyDefaults()
    53  	s := &store{
    54  		config:   config,
    55  		origins:  origins,
    56  		provider: provider,
    57  	}
    58  	s.locations = dedup.NewLimiter(clk, &locations{s})
    59  	s.peerContexts = dedup.NewLimiter(clk, &peerContexts{s})
    60  	return s
    61  }
    62  
    63  func (s *store) GetOrigins(d core.Digest) ([]*core.PeerInfo, error) {
    64  	lr := s.locations.Run(d).(*locationsResult)
    65  	if lr.err != nil {
    66  		return nil, lr.err
    67  	}
    68  
    69  	var errs []error
    70  	var origins []*core.PeerInfo
    71  	for _, addr := range lr.addrs {
    72  		pcr := s.peerContexts.Run(addr).(*peerContextResult)
    73  		if pcr.err != nil {
    74  			errs = append(errs, pcr.err)
    75  		} else {
    76  			origins = append(origins, core.PeerInfoFromContext(pcr.pctx, true))
    77  		}
    78  	}
    79  	if len(origins) == 0 {
    80  		return nil, allUnavailableError{fmt.Errorf("all origins unavailable: %s", errutil.Join(errs))}
    81  	}
    82  	return origins, nil
    83  }
    84  
    85  type locations struct {
    86  	store *store
    87  }
    88  
    89  type locationsResult struct {
    90  	addrs []string
    91  	err   error
    92  }
    93  
    94  func (l *locations) Run(input interface{}) (interface{}, time.Duration) {
    95  	d := input.(core.Digest)
    96  	addrs, err := blobclient.Locations(l.store.provider, l.store.origins, d)
    97  	ttl := l.store.config.LocationsTTL
    98  	if err != nil {
    99  		ttl = l.store.config.LocationsErrorTTL
   100  	}
   101  	return &locationsResult{addrs, err}, ttl
   102  }
   103  
   104  type peerContexts struct {
   105  	store *store
   106  }
   107  
   108  type peerContextResult struct {
   109  	pctx core.PeerContext
   110  	err  error
   111  }
   112  
   113  func (p *peerContexts) Run(input interface{}) (interface{}, time.Duration) {
   114  	addr := input.(string)
   115  	pctx, err := p.store.provider.Provide(addr).GetPeerContext()
   116  	ttl := p.store.config.OriginContextTTL
   117  	if err != nil {
   118  		log.With("origin", addr).Errorf("Origin unavailable: %s", err)
   119  		ttl = p.store.config.OriginUnavailableTTL
   120  	}
   121  	return &peerContextResult{pctx, err}, ttl
   122  }