diff options
Diffstat (limited to 'web/searchix.go')
-rw-r--r-- | web/searchix.go | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/web/searchix.go b/web/searchix.go new file mode 100644 index 0000000..b410d8f --- /dev/null +++ b/web/searchix.go @@ -0,0 +1,156 @@ +package web + +import ( + "context" + "math" + "sync" + "time" + + "go.alanpearce.eu/searchix/internal/config" + "go.alanpearce.eu/searchix/internal/importer" + "go.alanpearce.eu/searchix/internal/index" + "go.alanpearce.eu/searchix/internal/server" + "go.alanpearce.eu/x/log" + + "github.com/getsentry/sentry-go" + "gitlab.com/tozd/go/errors" +) + +func nextUTCOccurrenceOfTime(dayTime config.LocalTime) time.Time { + now := time.Now() + nextRun := time.Date( + now.Year(), + now.Month(), + now.Day(), + dayTime.Hour, + dayTime.Minute, + dayTime.Second, + 0, + time.UTC, + ) + if nextRun.Before(now) { + return nextRun.AddDate(0, 0, 1) + } + + return nextRun +} + +type Server struct { + sv *server.Server + wg *sync.WaitGroup + cfg *config.Config + log *log.Logger + sentryHub *sentry.Hub + readIndex *index.ReadIndex + writeIndex *index.WriteIndex +} + +func New(cfg *config.Config, log *log.Logger) (*Server, errors.E) { + err := sentry.Init(sentry.ClientOptions{ + EnableTracing: true, + TracesSampleRate: 1.0, + Dsn: cfg.Web.SentryDSN, + Environment: cfg.Web.Environment, + }) + if err != nil { + log.Warn("could not initialise sentry", "error", err) + } + + return &Server{ + cfg: cfg, + log: log, + sentryHub: sentry.CurrentHub(), + }, nil +} + +func (s *Server) startUpdateTimer( + ctx context.Context, + cfg *config.Config, + localHub *sentry.Hub, +) { + const monitorSlug = "import" + localHub.WithScope(func(scope *sentry.Scope) { + var err errors.E + scope.SetContext("monitor", sentry.Context{"slug": monitorSlug}) + monitorConfig := &sentry.MonitorConfig{ + Schedule: sentry.IntervalSchedule(1, sentry.MonitorScheduleUnitDay), + MaxRuntime: int64(math.Ceil(cfg.Importer.Timeout.Minutes())), + CheckInMargin: 5, + Timezone: time.Local.String(), + } + + s.wg.Add(1) + nextRun := nextUTCOccurrenceOfTime(s.cfg.Importer.UpdateAt) + importer.SetNextRun(nextRun) + for { + s.log.Debug("scheduling next run", "next-run", nextRun) + select { + case <-ctx.Done(): + s.log.Debug("stopping scheduler") + s.wg.Done() + + return + case <-time.After(time.Until(nextRun)): + } + s.wg.Add(1) + s.log.Info("updating index") + + eventID := localHub.CaptureCheckIn(&sentry.CheckIn{ + MonitorSlug: monitorSlug, + Status: sentry.CheckInStatusInProgress, + }, monitorConfig) + importer.MarkIndexingStarted() + + imp := importer.New(s.cfg, s.log.Named("importer"), s.writeIndex) + err = imp.Start(ctx, false, nil) + s.wg.Done() + if err != nil { + s.log.Warn("error updating index", "error", err) + + localHub.CaptureException(err) + localHub.CaptureCheckIn(&sentry.CheckIn{ + ID: *eventID, + MonitorSlug: monitorSlug, + Status: sentry.CheckInStatusError, + }, monitorConfig) + } else { + s.log.Info("update complete") + + localHub.CaptureCheckIn(&sentry.CheckIn{ + ID: *eventID, + MonitorSlug: monitorSlug, + Status: sentry.CheckInStatusOK, + }, monitorConfig) + } + nextRun = nextRun.AddDate(0, 0, 1) + importer.MarkIndexingFinished(nextRun) + } + }) +} + +func (s *Server) Start(ctx context.Context, liveReload bool) errors.E { + var err errors.E + s.sv, err = server.New(s.cfg, s.readIndex, s.log.Named("server"), liveReload) + if err != nil { + return errors.Wrap(err, "error setting up server") + } + + s.wg = &sync.WaitGroup{} + go s.startUpdateTimer(ctx, s.cfg, sentry.CurrentHub().Clone()) + + s.wg.Add(1) + err = s.sv.Start() + if err != nil { + s.wg.Done() + + return errors.Wrap(err, "error starting server") + } + + return nil +} + +func (s *Server) Stop() { + <-s.sv.Stop() + defer s.wg.Done() + s.sentryHub.Flush(2 * time.Second) +} |