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 }