github.com/weaviate/weaviate@v1.24.6/entities/cyclemanager/cyclecallbackctrl.go (about) 1 // _ _ 2 // __ _____ __ ___ ___ __ _| |_ ___ 3 // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \ 4 // \ V V / __/ (_| |\ V /| | (_| | || __/ 5 // \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___| 6 // 7 // Copyright © 2016 - 2024 Weaviate B.V. All rights reserved. 8 // 9 // CONTACT: hello@weaviate.io 10 // 11 12 package cyclemanager 13 14 import ( 15 "context" 16 "sync" 17 18 "github.com/sirupsen/logrus" 19 enterrors "github.com/weaviate/weaviate/entities/errors" 20 21 "github.com/weaviate/weaviate/entities/errorcompounder" 22 ) 23 24 // Used to control of registered in CycleCallbacks container callback 25 // Allows deactivating and activating registered callback or unregistering it 26 type CycleCallbackCtrl interface { 27 IsActive() bool 28 Activate() error 29 Deactivate(ctx context.Context) error 30 Unregister(ctx context.Context) error 31 } 32 33 type cycleCallbackCtrl struct { 34 callbackId uint32 35 callbackCustomId string 36 37 isActive func(callbackId uint32, callbackCustomId string) bool 38 activate func(callbackId uint32, callbackCustomId string) error 39 deactivate func(ctx context.Context, callbackId uint32, callbackCustomId string) error 40 unregister func(ctx context.Context, callbackId uint32, callbackCustomId string) error 41 } 42 43 func (c *cycleCallbackCtrl) IsActive() bool { 44 return c.isActive(c.callbackId, c.callbackCustomId) 45 } 46 47 func (c *cycleCallbackCtrl) Activate() error { 48 return c.activate(c.callbackId, c.callbackCustomId) 49 } 50 51 func (c *cycleCallbackCtrl) Deactivate(ctx context.Context) error { 52 return c.deactivate(ctx, c.callbackId, c.callbackCustomId) 53 } 54 55 func (c *cycleCallbackCtrl) Unregister(ctx context.Context) error { 56 return c.unregister(ctx, c.callbackId, c.callbackCustomId) 57 } 58 59 type cycleCombinedCallbackCtrl struct { 60 routinesLimit int 61 ctrls []CycleCallbackCtrl 62 logger logrus.FieldLogger 63 } 64 65 // Creates combined controller to manage all provided controllers at once as it was single instance. 66 // Methods (activate, deactivate, unregister) calls nested controllers' methods in parallel by number of 67 // goroutines given as argument. If < 1 value given, NumCPU is used. 68 func NewCombinedCallbackCtrl(routinesLimit int, logger logrus.FieldLogger, ctrls ...CycleCallbackCtrl) CycleCallbackCtrl { 69 if routinesLimit <= 0 { 70 routinesLimit = _NUMCPU 71 } 72 73 return &cycleCombinedCallbackCtrl{routinesLimit: routinesLimit, logger: logger, ctrls: ctrls} 74 } 75 76 func (c *cycleCombinedCallbackCtrl) IsActive() bool { 77 for _, ctrl := range c.ctrls { 78 if !ctrl.IsActive() { 79 return false 80 } 81 } 82 return true 83 } 84 85 func (c *cycleCombinedCallbackCtrl) Activate() error { 86 return c.combineErrors(c.activate()...) 87 } 88 89 func (c *cycleCombinedCallbackCtrl) activate() []error { 90 eg := enterrors.NewErrorGroupWrapper(c.logger) 91 eg.SetLimit(c.routinesLimit) 92 lock := new(sync.Mutex) 93 94 errs := make([]error, 0, len(c.ctrls)) 95 for _, ctrl := range c.ctrls { 96 ctrl := ctrl 97 eg.Go(func() error { 98 if err := ctrl.Activate(); err != nil { 99 c.locked(lock, func() { errs = append(errs, err) }) 100 return err 101 } 102 return nil 103 }) 104 } 105 106 eg.Wait() 107 return errs 108 } 109 110 func (c *cycleCombinedCallbackCtrl) Deactivate(ctx context.Context) error { 111 errs, deactivated := c.deactivate(ctx) 112 if len(errs) == 0 { 113 return nil 114 } 115 116 // try activating back deactivated 117 eg := enterrors.NewErrorGroupWrapper(c.logger) 118 eg.SetLimit(c.routinesLimit) 119 for _, id := range deactivated { 120 id := id 121 eg.Go(func() error { 122 return c.ctrls[id].Activate() 123 }) 124 } 125 126 eg.Wait() 127 return c.combineErrors(errs...) 128 } 129 130 func (c *cycleCombinedCallbackCtrl) deactivate(ctx context.Context) ([]error, []int) { 131 eg := enterrors.NewErrorGroupWrapper(c.logger) 132 eg.SetLimit(c.routinesLimit) 133 lock := new(sync.Mutex) 134 135 errs := make([]error, 0, len(c.ctrls)) 136 deactivated := make([]int, 0, len(c.ctrls)) 137 for id, ctrl := range c.ctrls { 138 id, ctrl := id, ctrl 139 eg.Go(func() error { 140 if err := ctrl.Deactivate(ctx); err != nil { 141 c.locked(lock, func() { errs = append(errs, err) }) 142 return err 143 } 144 c.locked(lock, func() { deactivated = append(deactivated, id) }) 145 return nil 146 }, id, ctrl) 147 } 148 149 eg.Wait() 150 return errs, deactivated 151 } 152 153 func (c *cycleCombinedCallbackCtrl) Unregister(ctx context.Context) error { 154 return c.combineErrors(c.unregister(ctx)...) 155 } 156 157 func (c *cycleCombinedCallbackCtrl) unregister(ctx context.Context) []error { 158 eg := enterrors.NewErrorGroupWrapper(c.logger) 159 eg.SetLimit(c.routinesLimit) 160 lock := new(sync.Mutex) 161 162 errs := make([]error, 0, len(c.ctrls)) 163 for _, ctrl := range c.ctrls { 164 ctrl := ctrl 165 eg.Go(func() error { 166 if err := ctrl.Unregister(ctx); err != nil { 167 c.locked(lock, func() { errs = append(errs, err) }) 168 return err 169 } 170 return nil 171 }) 172 } 173 174 eg.Wait() 175 return errs 176 } 177 178 func (c *cycleCombinedCallbackCtrl) locked(lock *sync.Mutex, mutate func()) { 179 lock.Lock() 180 defer lock.Unlock() 181 182 mutate() 183 } 184 185 func (c *cycleCombinedCallbackCtrl) combineErrors(errors ...error) error { 186 ec := &errorcompounder.ErrorCompounder{} 187 for _, err := range errors { 188 ec.Add(err) 189 } 190 return ec.ToError() 191 } 192 193 type cycleCallbackCtrlNoop struct{} 194 195 func NewCallbackCtrlNoop() CycleCallbackCtrl { 196 return &cycleCallbackCtrlNoop{} 197 } 198 199 func (c *cycleCallbackCtrlNoop) IsActive() bool { 200 return false 201 } 202 203 func (c *cycleCallbackCtrlNoop) Activate() error { 204 return nil 205 } 206 207 func (c *cycleCallbackCtrlNoop) Deactivate(ctx context.Context) error { 208 return ctx.Err() 209 } 210 211 func (c *cycleCallbackCtrlNoop) Unregister(ctx context.Context) error { 212 return ctx.Err() 213 }