any-sync/util/periodicsync/periodicsync.go

79 lines
1.7 KiB
Go

//go:generate mockgen -destination mock_periodicsync/mock_periodicsync.go github.com/anytypeio/any-sync/util/periodicsync PeriodicSync
package periodicsync
import (
"context"
"github.com/anytypeio/any-sync/app/logger"
"go.uber.org/zap"
"time"
)
type PeriodicSync interface {
Run()
Close()
}
type SyncerFunc func(ctx context.Context) error
func NewPeriodicSync(periodSeconds int, timeout time.Duration, syncer SyncerFunc, l logger.CtxLogger) PeriodicSync {
ctx, cancel := context.WithCancel(context.Background())
ctx = logger.CtxWithFields(ctx, zap.String("rootOp", "periodicSync"))
return &periodicSync{
syncer: syncer,
log: l,
syncCtx: ctx,
syncCancel: cancel,
syncLoopDone: make(chan struct{}),
periodSeconds: periodSeconds,
timeout: timeout,
}
}
type periodicSync struct {
log logger.CtxLogger
syncer SyncerFunc
syncCtx context.Context
syncCancel context.CancelFunc
syncLoopDone chan struct{}
periodSeconds int
timeout time.Duration
}
func (p *periodicSync) Run() {
go p.syncLoop(p.periodSeconds)
}
func (p *periodicSync) syncLoop(periodSeconds int) {
period := time.Duration(periodSeconds) * time.Second
defer close(p.syncLoopDone)
doSync := func() {
ctx := p.syncCtx
if p.timeout != 0 {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(p.syncCtx, p.timeout)
defer cancel()
}
if err := p.syncer(ctx); err != nil {
p.log.Warn("periodic sync error", zap.Error(err))
}
}
doSync()
if period > 0 {
ticker := time.NewTicker(period)
defer ticker.Stop()
for {
select {
case <-p.syncCtx.Done():
return
case <-ticker.C:
doSync()
}
}
}
}
func (p *periodicSync) Close() {
p.syncCancel()
<-p.syncLoopDone
}