Files
kurious/internal/kurious/ports/background.go
2023-12-15 13:00:59 +03:00

132 lines
3.1 KiB
Go

package ports
import (
"context"
"encoding/json"
"fmt"
"log/slog"
"strings"
"time"
"git.loyso.art/frx/kurious/internal/common/client/sravni"
"git.loyso.art/frx/kurious/internal/common/errors"
"git.loyso.art/frx/kurious/internal/common/nullable"
"git.loyso.art/frx/kurious/internal/common/xcontext"
"git.loyso.art/frx/kurious/internal/common/xlog"
"git.loyso.art/frx/kurious/internal/kurious/ports/background"
"git.loyso.art/frx/kurious/internal/kurious/service"
"github.com/robfig/cron/v3"
)
type BackgroundProcess struct {
scheduler *cron.Cron
log *slog.Logger
syncSravniHandlerEntryID nullable.Value[cron.EntryID]
syncSravniHandler handler
}
func NewBackgroundProcess(ctx context.Context, log *slog.Logger) *BackgroundProcess {
clog := xlog.WrapSLogger(ctx, log)
scheduler := cron.New(
cron.WithSeconds(),
cron.WithChain(
cron.Recover(clog),
),
)
bp := &BackgroundProcess{
scheduler: scheduler,
log: log,
}
return bp
}
func (bp *BackgroundProcess) RegisterSyncSravniHandler(
ctx context.Context,
svc service.Application,
client sravni.Client,
cronValue string,
) (err error) {
const handlerName = "sync_sravni_handler"
bp.syncSravniHandler = background.NewSyncSravniHandler(svc, client, bp.log)
if cronValue == "" {
return nil
}
bp.syncSravniHandlerEntryID, err = bp.registerHandler(ctx, cronValue, handlerName, bp.syncSravniHandler)
return err
}
func (bp *BackgroundProcess) ForceExecuteSyncSravniHandler(ctx context.Context) error {
if bp.syncSravniHandler == nil {
return errors.SimpleError("sync sravni handler not mounted")
}
return bp.syncSravniHandler.Handle(ctx)
}
type NextRunStats map[string]time.Time
func (s NextRunStats) String() string {
var sb strings.Builder
_ = json.NewEncoder(&sb).Encode(s)
return sb.String()
}
func (bp *BackgroundProcess) GetNextRunStats() NextRunStats {
out := make(NextRunStats)
if bp.syncSravniHandlerEntryID.Valid() {
entryID := bp.syncSravniHandlerEntryID.Value()
sEntry := bp.scheduler.Entry(entryID)
out["sravni_handler"] = sEntry.Next
}
return NextRunStats(out)
}
func (bp *BackgroundProcess) Run() {
bp.scheduler.Run()
}
func (bp *BackgroundProcess) Shutdown(ctx context.Context) error {
sdctx := bp.scheduler.Stop()
select {
case <-ctx.Done():
return ctx.Err()
case <-sdctx.Done():
return nil
}
}
type handler interface {
Handle(context.Context) error
}
func (bp *BackgroundProcess) registerHandler(ctx context.Context, spec, name string, h handler) (nullable.Value[cron.EntryID], error) {
handlerField := slog.String("handler", name)
jctx := xcontext.WithLogFields(ctx, handlerField)
xcontext.LogInfo(jctx, bp.log, "registering handler", handlerField)
entry, err := bp.scheduler.AddJob(spec, cron.FuncJob(func() {
err := h.Handle(jctx)
if err != nil {
xcontext.LogWithError(jctx, bp.log, err, "unable to run iteration")
}
xcontext.LogInfo(jctx, bp.log, "iteration completed")
}))
if err != nil {
return nullable.Value[cron.EntryID]{}, fmt.Errorf("adding %s job: %w", name, err)
}
var out nullable.Value[cron.EntryID]
out.Set(entry)
return out, nil
}