github.com/whiteboxio/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/corev1alpha1/actor/replicator.go (about)

     1  package actor
     2  
     3  import (
     4  	"fmt"
     5  	"math/bits"
     6  	"math/rand"
     7  	"sync"
     8  	"time"
     9  
    10  	core "github.com/awesome-flow/flow/pkg/corev1alpha1"
    11  )
    12  
    13  const (
    14  	MaxPeersCnt = 64
    15  	ReplTimeout = 50 * time.Millisecond
    16  )
    17  
    18  type ReplicateMode uint8
    19  
    20  const (
    21  	ReplicateUnknown ReplicateMode = iota
    22  	ReplicateFanout
    23  	ReplicateRand
    24  	ReplicateNcopy
    25  	ReplicateEach
    26  )
    27  
    28  type Replicator struct {
    29  	name      string
    30  	ctx       *core.Context
    31  	maskfunc  func(uint64, int) uint64
    32  	queueIn   chan *core.Message
    33  	queuesOut []chan *core.Message
    34  	lock      sync.Mutex
    35  	wg        sync.WaitGroup
    36  }
    37  
    38  var _ core.Actor = (*Replicator)(nil)
    39  
    40  func NewReplicator(name string, ctx *core.Context, params core.Params) (core.Actor, error) {
    41  	mode, ok := params["mode"]
    42  	if !ok {
    43  		return nil, fmt.Errorf("replicator %s is missing `mode` config", name)
    44  	}
    45  
    46  	r := &Replicator{
    47  		name:      name,
    48  		ctx:       ctx,
    49  		queueIn:   make(chan *core.Message),
    50  		queuesOut: make([]chan *core.Message, 0),
    51  	}
    52  
    53  	var maskfunc func(uint64, int) uint64
    54  	switch mode.(string) {
    55  	case "fanout":
    56  		maskfunc = maskFanout
    57  	case "rand":
    58  		maskfunc = maskRand
    59  	case "ncopy":
    60  		maskfunc = maskNcopy
    61  	case "each":
    62  		maskfunc = maskEach
    63  	default:
    64  		return nil, fmt.Errorf("replicator %s `mode` is unknown: %s", name, mode.(string))
    65  	}
    66  
    67  	r.maskfunc = maskfunc
    68  
    69  	return r, nil
    70  }
    71  
    72  func maskFanout(mask uint64, lenq int) uint64 {
    73  	if lenq == 0 {
    74  		return 0
    75  	}
    76  	bshift := uint64((1 << uint64(lenq)) - 1)
    77  	mask &= bshift
    78  	mask = ((mask << 1) | (mask >> (uint64(lenq) - 1))) & bshift
    79  	if mask == 0 {
    80  		mask = 1
    81  	}
    82  	return mask
    83  }
    84  
    85  func maskRand(mask uint64, lenq int) uint64 {
    86  	return 1 << uint64(rand.Int63n(int64(lenq)))
    87  }
    88  
    89  func maskNcopy(uint64, int) uint64 {
    90  	panic("not implemented")
    91  }
    92  
    93  func maskEach(mask uint64, lenq int) uint64 {
    94  	return (1 << uint64(lenq)) - 1
    95  }
    96  
    97  func (r *Replicator) replicate(msg *core.Message, mask uint64) error {
    98  	wg := sync.WaitGroup{}
    99  	ix := 0
   100  	cnt := bits.OnesCount64(mask)
   101  	res := make(chan core.MsgStatus, cnt)
   102  	defer close(res)
   103  	for mask > 0 {
   104  		if mask&0x1 == 1 {
   105  			wg.Add(1)
   106  			go func(ix int) {
   107  				msgcp := msg.Copy()
   108  				r.queuesOut[ix] <- msgcp
   109  				select {
   110  				case s := <-msgcp.AwaitChan():
   111  					res <- s
   112  				case <-time.After(ReplTimeout):
   113  					msg.Complete(core.MsgStatusTimedOut)
   114  					res <- core.MsgStatusTimedOut
   115  				}
   116  				wg.Done()
   117  			}(ix)
   118  		}
   119  		ix++
   120  		mask >>= 1
   121  	}
   122  	wg.Wait()
   123  	var compsts uint8
   124  	var s core.MsgStatus
   125  	for i := 0; i < cnt; i++ {
   126  		s = <-res
   127  		switch s {
   128  		case core.MsgStatusDone:
   129  			compsts |= 1 << 0
   130  		case core.MsgStatusPartialSend:
   131  			compsts |= 1 << 1
   132  		case core.MsgStatusTimedOut:
   133  			compsts |= 1 << 2
   134  		default:
   135  			compsts |= 1 << 3
   136  		}
   137  	}
   138  	if compsts <= 1 { // 0 stands for no-send (0-mask)
   139  		msg.Complete(core.MsgStatusDone)
   140  	} else if compsts>>1 == 1 {
   141  		msg.Complete(core.MsgStatusPartialSend)
   142  	} else if compsts>>2 == 1 {
   143  		msg.Complete(core.MsgStatusTimedOut)
   144  	} else {
   145  		msg.Complete(core.MsgStatusFailed)
   146  	}
   147  
   148  	return nil
   149  }
   150  
   151  func (r *Replicator) Name() string {
   152  	return r.name
   153  }
   154  
   155  func (r *Replicator) Start() error {
   156  	go func() {
   157  		var mask uint64
   158  		for msg := range r.queueIn {
   159  			mask = r.maskfunc(mask, len(r.queuesOut))
   160  			if err := r.replicate(msg, mask); err != nil {
   161  				msg.Complete(core.MsgStatusFailed)
   162  			}
   163  		}
   164  	}()
   165  
   166  	return nil
   167  }
   168  
   169  func (r *Replicator) Stop() error {
   170  	r.lock.Lock()
   171  	defer r.lock.Unlock()
   172  
   173  	close(r.queueIn)
   174  	for _, q := range r.queuesOut {
   175  		close(q)
   176  	}
   177  	r.wg.Wait()
   178  
   179  	return nil
   180  }
   181  
   182  func (r *Replicator) Connect(nthreads int, peer core.Receiver) error {
   183  	r.lock.Lock()
   184  	defer r.lock.Unlock()
   185  
   186  	if len(r.queuesOut) >= MaxPeersCnt {
   187  		return fmt.Errorf("replicator has achieved the max # of connections: %d", len(r.queuesOut))
   188  	}
   189  
   190  	q := make(chan *core.Message)
   191  	for i := 0; i < nthreads; i++ {
   192  		r.wg.Add(1)
   193  		go func() {
   194  			for msg := range q {
   195  				if err := peer.Receive(msg); err != nil {
   196  					msg.Complete(core.MsgStatusFailed)
   197  					r.ctx.Logger().Error(err.Error())
   198  				}
   199  			}
   200  			r.wg.Done()
   201  		}()
   202  	}
   203  	r.queuesOut = append(r.queuesOut, q)
   204  
   205  	return nil
   206  }
   207  
   208  func (r *Replicator) Receive(msg *core.Message) error {
   209  	r.queueIn <- msg
   210  
   211  	return nil
   212  }