github.com/wfusion/gofusion@v1.1.14/common/infra/asynq/syncer.go (about)

     1  // Copyright 2020 Kentaro Hibino. All rights reserved.
     2  // Use of this source code is governed by a MIT license
     3  // that can be found in the LICENSE file.
     4  
     5  package asynq
     6  
     7  import (
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/wfusion/gofusion/common/infra/asynq/pkg/log"
    12  )
    13  
    14  // syncer is responsible for queuing up failed requests to redis and retry
    15  // those requests to sync state between the background process and redis.
    16  type syncer struct {
    17  	logger *log.Logger
    18  
    19  	requestsCh <-chan *syncRequest
    20  
    21  	// channel to communicate back to the long running "syncer" goroutine.
    22  	done chan struct{}
    23  
    24  	// interval between sync operations.
    25  	interval time.Duration
    26  }
    27  
    28  type syncRequest struct {
    29  	fn       func() error // sync operation
    30  	errMsg   string       // error message
    31  	deadline time.Time    // request should be dropped if deadline has been exceeded
    32  }
    33  
    34  type syncerParams struct {
    35  	logger     *log.Logger
    36  	requestsCh <-chan *syncRequest
    37  	interval   time.Duration
    38  }
    39  
    40  func newSyncer(params syncerParams) *syncer {
    41  	return &syncer{
    42  		logger:     params.logger,
    43  		requestsCh: params.requestsCh,
    44  		done:       make(chan struct{}),
    45  		interval:   params.interval,
    46  	}
    47  }
    48  
    49  func (s *syncer) shutdown() {
    50  	s.logger.Debug("Syncer shutting down...")
    51  	// Signal the syncer goroutine to stop.
    52  	s.done <- struct{}{}
    53  }
    54  
    55  func (s *syncer) start(wg *sync.WaitGroup) {
    56  	wg.Add(1)
    57  	go func() {
    58  		defer wg.Done()
    59  		var requests []*syncRequest
    60  		for {
    61  			select {
    62  			case <-s.done:
    63  				// Try sync one last time before shutting down.
    64  				for _, req := range requests {
    65  					if err := req.fn(); err != nil {
    66  						s.logger.Error(req.errMsg)
    67  					}
    68  				}
    69  				s.logger.Debug("Syncer done")
    70  				return
    71  			case req := <-s.requestsCh:
    72  				requests = append(requests, req)
    73  			case <-time.After(s.interval):
    74  				var temp []*syncRequest
    75  				for _, req := range requests {
    76  					if req.deadline.Before(time.Now()) {
    77  						continue // drop stale request
    78  					}
    79  					if err := req.fn(); err != nil {
    80  						temp = append(temp, req)
    81  					}
    82  				}
    83  				requests = temp
    84  			}
    85  		}
    86  	}()
    87  }