github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/util/store.go (about)

     1  /*
     2  Copyright 2020 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package util
    18  
    19  import (
    20  	"fmt"
    21  	"sync"
    22  
    23  	"github.com/golang/groupcache/singleflight"
    24  )
    25  
    26  // SyncStore exports a single method `Exec` to ensure single execution of a function
    27  // and share the result between all callers of the function.
    28  type SyncStore struct {
    29  	sf      singleflight.Group
    30  	results sync.Map
    31  }
    32  
    33  // Exec executes the function f if and only if it's being called the first time for a specific key.
    34  // If it's called multiple times for the same key only the first call will execute and store the result of f.
    35  // All other calls will be blocked until the running instance of f returns and all of them receive the same result.
    36  func (o *SyncStore) Exec(key string, f func() interface{}) interface{} {
    37  	val, err := o.sf.Do(key, func() (_ interface{}, err error) {
    38  		// trap any runtime error due to synchronization issues.
    39  		defer func() {
    40  			if rErr := recover(); rErr != nil {
    41  				err = retrieveError(key, rErr)
    42  			}
    43  		}()
    44  		v, ok := o.results.Load(key)
    45  		if !ok {
    46  			v = f()
    47  			o.results.Store(key, v)
    48  		}
    49  		return v, nil
    50  	})
    51  	if err != nil {
    52  		return err
    53  	}
    54  	return val
    55  }
    56  
    57  // Store will store the results for a key in a cache
    58  // This function is not safe to use if multiple subroutines store the
    59  // result for the same key.
    60  func (o *SyncStore) Store(key string, r interface{}) {
    61  	o.results.Store(key, r)
    62  }
    63  
    64  // NewSyncStore returns a new instance of `SyncStore`
    65  func NewSyncStore() *SyncStore {
    66  	return &SyncStore{
    67  		sf:      singleflight.Group{},
    68  		results: sync.Map{},
    69  	}
    70  }
    71  
    72  // StoreError represent any error that when retrieving errors from the store.
    73  type StoreError struct {
    74  	message string
    75  }
    76  
    77  func (e StoreError) Error() string {
    78  	return e.message
    79  }
    80  
    81  func retrieveError(key string, i interface{}) StoreError {
    82  	return StoreError{
    83  		message: fmt.Sprintf("internal error retrieving cached results for key %s: %v", key, i),
    84  	}
    85  }