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  }