github.com/cilium/cilium@v1.16.2/pkg/kvstore/store/watchstoremgr.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package store 5 6 import ( 7 "context" 8 "path" 9 "sync" 10 "sync/atomic" 11 12 "github.com/sirupsen/logrus" 13 14 "github.com/cilium/cilium/pkg/kvstore" 15 "github.com/cilium/cilium/pkg/logging/logfields" 16 ) 17 18 // WSMFunc if a function which can be registered in the WatchStoreManager. 19 type WSMFunc func(context.Context) 20 21 // WatchStoreManager enables to register a set of functions to be asynchronously 22 // executed when the corresponding kvstore prefixes are synchronized (based on 23 // the implementation). 24 type WatchStoreManager interface { 25 // Register registers a function associated with a given kvstore prefix. 26 // It cannot be called once Run() has started. 27 Register(prefix string, function WSMFunc) 28 // Run starts the manager, blocking until the context is closed and all 29 // started functions terminated. 30 Run(ctx context.Context) 31 } 32 33 // wsmCommon implements the common logic shared by WatchStoreManager implementations. 34 type wsmCommon struct { 35 wg sync.WaitGroup 36 functions map[string]WSMFunc 37 38 running atomic.Bool 39 log *logrus.Entry 40 } 41 42 func newWSMCommon(clusterName string) wsmCommon { 43 return wsmCommon{ 44 functions: make(map[string]WSMFunc), 45 log: log.WithField(logfields.ClusterName, clusterName), 46 } 47 } 48 49 // Register registers a function associated with a given kvstore prefix. 50 // It cannot be called once Run() has started. 51 func (mgr *wsmCommon) Register(prefix string, function WSMFunc) { 52 if mgr.running.Load() { 53 mgr.log.Panic("Cannot call Register while the watch store manager is running") 54 } 55 56 mgr.functions[prefix] = function 57 } 58 59 func (mgr *wsmCommon) ready(ctx context.Context, prefix string) { 60 if fn := mgr.functions[prefix]; fn != nil { 61 mgr.log.WithField(logfields.Prefix, prefix).Debug("Starting function for kvstore prefix") 62 delete(mgr.functions, prefix) 63 64 mgr.wg.Add(1) 65 go func() { 66 defer mgr.wg.Done() 67 fn(ctx) 68 mgr.log.WithField(logfields.Prefix, prefix).Debug("Function terminated for kvstore prefix") 69 }() 70 } else { 71 mgr.log.WithField(logfields.Prefix, prefix).Debug("Received sync event for unregistered prefix") 72 } 73 } 74 75 func (mgr *wsmCommon) run() { 76 mgr.log.Info("Starting watch store manager") 77 if mgr.running.Swap(true) { 78 mgr.log.Panic("Cannot start the watch store manager twice") 79 } 80 } 81 82 func (mgr *wsmCommon) wait() { 83 mgr.wg.Wait() 84 mgr.log.Info("Stopped watch store manager") 85 } 86 87 type wsmSync struct { 88 wsmCommon 89 90 clusterName string 91 backend WatchStoreBackend 92 store WatchStore 93 onUpdate func(prefix string) 94 } 95 96 // NewWatchStoreManagerSync implements the WatchStoreManager interface, starting the 97 // registered functions only once the corresponding prefix sync canary has been received. 98 // This ensures that the synchronization of the keys hosted under the given prefix 99 // have been successfully synchronized from the external source, even in case an 100 // ephemeral kvstore is used. 101 func newWatchStoreManagerSync(backend WatchStoreBackend, clusterName string, factory Factory) WatchStoreManager { 102 mgr := wsmSync{ 103 wsmCommon: newWSMCommon(clusterName), 104 clusterName: clusterName, 105 backend: backend, 106 } 107 108 mgr.store = factory.NewWatchStore(clusterName, KVPairCreator, &mgr) 109 return &mgr 110 } 111 112 // Run starts the manager, blocking until the context is closed and all 113 // started functions terminated. 114 func (mgr *wsmSync) Run(ctx context.Context) { 115 mgr.run() 116 mgr.onUpdate = func(prefix string) { mgr.ready(ctx, prefix) } 117 mgr.store.Watch(ctx, mgr.backend, path.Join(kvstore.SyncedPrefix, mgr.clusterName)) 118 mgr.wait() 119 } 120 121 func (mgr *wsmSync) OnUpdate(k Key) { mgr.onUpdate(k.GetKeyName()) } 122 func (mgr *wsmSync) OnDelete(k NamedKey) {} 123 124 type wsmImmediate struct { 125 wsmCommon 126 } 127 128 // NewWatchStoreManagerImmediate implements the WatchStoreManager interface, 129 // immediately starting the registered functions once Run() is executed. 130 func NewWatchStoreManagerImmediate(clusterName string) WatchStoreManager { 131 return &wsmImmediate{ 132 wsmCommon: newWSMCommon(clusterName), 133 } 134 } 135 136 // Run starts the manager, blocking until the context is closed and all 137 // started functions terminated. 138 func (mgr *wsmImmediate) Run(ctx context.Context) { 139 mgr.run() 140 for prefix := range mgr.functions { 141 mgr.ready(ctx, prefix) 142 } 143 mgr.wait() 144 }