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 }