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

     1  package actor
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/awesome-flow/flow/pkg/cfg"
     8  	core "github.com/awesome-flow/flow/pkg/corev1alpha1"
     9  	"github.com/awesome-flow/flow/pkg/types"
    10  )
    11  
    12  const (
    13  	minbackoff = 50 * time.Millisecond
    14  	maxbackoff = 5 * time.Second
    15  	maxretries = 0
    16  )
    17  
    18  type SinkCfg struct {
    19  	minbackoff time.Duration
    20  	maxbackoff time.Duration
    21  	maxretries int
    22  }
    23  
    24  func NewSinkCfg(params core.Params) (*SinkCfg, error) {
    25  	cfg := &SinkCfg{
    26  		minbackoff: minbackoff,
    27  		maxbackoff: maxbackoff,
    28  		maxretries: maxretries,
    29  	}
    30  	if v, ok := params["max_retries"]; ok {
    31  		cfg.maxretries = v.(int)
    32  	}
    33  	if v, ok := params["min_backoff"]; ok {
    34  		cfg.minbackoff = time.Duration(v.(int)) * time.Millisecond
    35  	}
    36  	if v, ok := params["max_backoff"]; ok {
    37  		cfg.maxbackoff = time.Duration(v.(int)) * time.Millisecond
    38  	}
    39  
    40  	return cfg, nil
    41  }
    42  
    43  type Sink struct {
    44  	name      string
    45  	ctx       *core.Context
    46  	cfg       *SinkCfg
    47  	head      SinkHead
    48  	queue     chan *core.Message
    49  	reconnect chan chan struct{}
    50  	done      chan struct{}
    51  }
    52  
    53  var _ core.Actor = (*Sink)(nil)
    54  
    55  func NewSink(name string, ctx *core.Context, params core.Params) (core.Actor, error) {
    56  	cfg, err := NewSinkCfg(params)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("failed to initialize sink %q: %s", name, err)
    59  	}
    60  	head, err := SinkHeadFactory(params)
    61  	if err != nil {
    62  		return nil, fmt.Errorf("failed to initialize sink %q: %s", name, err)
    63  	}
    64  
    65  	return &Sink{
    66  		name:      name,
    67  		ctx:       ctx,
    68  		cfg:       cfg,
    69  		head:      head,
    70  		queue:     make(chan *core.Message),
    71  		reconnect: make(chan chan struct{}),
    72  		done:      make(chan struct{}),
    73  	}, nil
    74  }
    75  
    76  func (s *Sink) Name() string {
    77  	return s.name
    78  }
    79  
    80  func (s *Sink) doConnectHead(notify chan struct{}) error {
    81  	isdone := false
    82  	go func() {
    83  		select {
    84  		case <-s.done:
    85  			isdone = true
    86  		case <-notify:
    87  		}
    88  	}()
    89  	backoff := minbackoff
    90  	retried := 0
    91  	for !isdone {
    92  		if err := s.head.Connect(); err != nil {
    93  			s.ctx.Logger().Error("sink %q failed to reconnect: %s", s.name, err)
    94  			if mr := s.cfg.maxretries; mr > 0 && retried >= mr {
    95  				return fmt.Errorf("gave up after %d retries", retried)
    96  			}
    97  			s.ctx.Logger().Trace("sink %q will try to reconnect in %dms", s.name, backoff/1000000)
    98  			time.Sleep(backoff)
    99  			if backoff < s.cfg.maxbackoff {
   100  				backoff *= 2
   101  			}
   102  			retried++
   103  			continue
   104  
   105  		}
   106  		close(notify)
   107  		break
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func (s *Sink) Start() error {
   114  	nthreads, ok := s.ctx.Config().Get(types.NewKey(cfg.SystemMaxprocs))
   115  	if !ok {
   116  		return fmt.Errorf("failed to fetch `%s` config", cfg.SystemMaxprocs)
   117  	}
   118  
   119  	var reqreconn = func() {
   120  		// reconnect routine will close the
   121  		// notify channel
   122  		notify := make(chan struct{})
   123  		s.reconnect <- notify
   124  		<-notify
   125  	}
   126  
   127  	go func() {
   128  		for notify := range s.reconnect {
   129  			if err := s.doConnectHead(notify); err != nil {
   130  				// Fatal error here: giving up and crashing
   131  				s.ctx.Logger().Fatal("sink %q failed to reconnect: %s", s.name, err)
   132  			}
   133  		}
   134  	}()
   135  
   136  	for i := 0; i < nthreads.(int); i++ {
   137  		go func() {
   138  			for msg := range s.queue {
   139  				if _, err, rec := s.head.Write(msg.Body()); err != nil {
   140  					s.ctx.Logger().Error("sink %q failed to send message: %s", s.name, err)
   141  					msg.Complete(core.MsgStatusFailed)
   142  					if rec {
   143  						reqreconn()
   144  					}
   145  					continue
   146  				}
   147  				msg.Complete(core.MsgStatusDone)
   148  			}
   149  		}()
   150  	}
   151  
   152  	if err := s.head.Start(); err != nil {
   153  		return err
   154  	}
   155  
   156  	reqreconn()
   157  
   158  	return nil
   159  }
   160  
   161  func (s *Sink) Stop() error {
   162  	if err := s.head.Stop(); err != nil {
   163  		return err
   164  	}
   165  	close(s.queue)
   166  	close(s.reconnect)
   167  	close(s.done)
   168  
   169  	return nil
   170  }
   171  
   172  func (s *Sink) Connect(int, core.Receiver) error {
   173  	return fmt.Errorf("sink %q can not connect to other receivers", s.name)
   174  }
   175  
   176  func (s *Sink) Receive(msg *core.Message) error {
   177  	s.queue <- msg
   178  	return nil
   179  }