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  }