github.com/Axway/agent-sdk@v1.1.101/pkg/agent/poller/poller.go (about) 1 package poller 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 "time" 8 9 "github.com/Axway/agent-sdk/pkg/agent/events" 10 "github.com/Axway/agent-sdk/pkg/harvester" 11 "github.com/Axway/agent-sdk/pkg/util/log" 12 "github.com/Axway/agent-sdk/pkg/watchmanager/proto" 13 ) 14 15 type pollExecutor struct { 16 harvester harvester.Harvest 17 sequence events.SequenceProvider 18 topicSelfLink string 19 logger log.FieldLogger 20 timer *time.Timer 21 ctx context.Context 22 cancel context.CancelFunc 23 interval time.Duration 24 onStop onClientStopCb 25 isReady bool 26 lock sync.RWMutex 27 } 28 29 type newPollExecutorFunc func(interval time.Duration, options ...executorOpt) *pollExecutor 30 31 func newPollExecutor(interval time.Duration, options ...executorOpt) *pollExecutor { 32 logger := log.NewFieldLogger(). 33 WithComponent("pollExecutor"). 34 WithPackage("sdk.agent.poller") 35 36 ctx, cancel := context.WithCancel(context.Background()) 37 38 pm := &pollExecutor{ 39 logger: logger, 40 timer: time.NewTimer(interval), 41 ctx: ctx, 42 cancel: cancel, 43 interval: interval, 44 } 45 46 for _, opt := range options { 47 opt(pm) 48 } 49 50 return pm 51 } 52 53 // RegisterWatch registers a watch topic for polling events and publishing events on a channel 54 func (m *pollExecutor) RegisterWatch(eventChan chan *proto.Event, errChan chan error) { 55 m.logger.Trace("register watch topic for polling and publishing events") 56 if m.harvester == nil { 57 go func() { 58 m.Stop() 59 errChan <- fmt.Errorf("harvester is not configured for the polling client") 60 }() 61 return 62 } 63 64 if m.sequence.GetSequence() < 0 { 65 m.onHarvesterErr() 66 go func() { 67 m.Stop() 68 errChan <- fmt.Errorf("do not have a sequence id, stopping poller") 69 }() 70 return 71 } 72 73 if err := m.harvester.EventCatchUp(m.topicSelfLink, eventChan); err != nil { 74 m.logger.WithError(err).Error("harvester returned an error when syncing events") 75 m.onHarvesterErr() 76 go func() { 77 m.Stop() 78 errChan <- err 79 }() 80 return 81 } 82 83 m.lock.Lock() 84 m.isReady = true 85 m.lock.Unlock() 86 87 go func() { 88 err := m.sync(m.topicSelfLink, eventChan) 89 m.Stop() 90 errChan <- err 91 }() 92 } 93 94 func (m *pollExecutor) sync(topicSelfLink string, eventChan chan *proto.Event) error { 95 m.logger.Trace("sync events") 96 97 for { 98 select { 99 case <-m.ctx.Done(): 100 m.logger.Info("harvester polling has been stopped") 101 return nil 102 case <-m.timer.C: 103 if err := m.tick(topicSelfLink, eventChan); err != nil { 104 return err 105 } 106 } 107 } 108 } 109 110 func (m *pollExecutor) tick(topicSelfLink string, eventChan chan *proto.Event) (ret error) { 111 sequence := m.sequence.GetSequence() 112 logger := m.logger.WithField("sequence-id", sequence) 113 logger.Debug("retrieving harvester events") 114 115 defer func() { 116 if ret == nil { 117 m.timer.Reset(m.interval) 118 } 119 }() 120 121 if lastSeqID, err := m.harvester.ReceiveSyncEvents(topicSelfLink, sequence, eventChan); err != nil { 122 if _, ok := err.(*harvester.ErrSeqGone); ok { 123 m.sequence.SetSequence(lastSeqID) 124 return 125 } 126 127 logger.WithError(err).Error("harvester returned an error when syncing events") 128 m.onHarvesterErr() 129 ret = err 130 } 131 132 return 133 } 134 135 func (m *pollExecutor) onHarvesterErr() { 136 if m.onStop == nil { 137 return 138 } 139 140 m.onStop() 141 } 142 143 // Stop stops the poller 144 func (m *pollExecutor) Stop() { 145 m.timer.Stop() 146 m.cancel() 147 148 m.lock.Lock() 149 defer m.lock.Unlock() 150 m.isReady = false 151 152 m.logger.Debug("poller has been stopped") 153 } 154 155 // Status returns a bool indicating the status of the poller 156 func (m *pollExecutor) Status() bool { 157 m.lock.RLock() 158 defer m.lock.RUnlock() 159 if m.ctx.Err() != nil { 160 return false 161 } 162 163 return m.isReady 164 }