package main import "time" type Job interface { Schedule() Stop() Run() } type jobIdentifier int const ( JobIdCloseDecisions jobIdentifier = iota JobIdRemindVotersJob ) var rescheduleChannel = make(chan jobIdentifier, 1) func JobScheduler(quitChannel chan int) { var jobs = map[jobIdentifier]Job{ JobIdCloseDecisions: NewCloseDecisionsJob(), JobIdRemindVotersJob: NewRemindVotersJob(), } log.Info("started job scheduler") for { select { case jobId := <-rescheduleChannel: job := jobs[jobId] log.Infof("reschedule job %s", job) job.Schedule() case <-quitChannel: for _, job := range jobs { job.Stop() } log.Info("stop job scheduler") return } } } type CloseDecisionsJob struct { timer *time.Timer } func NewCloseDecisionsJob() *CloseDecisionsJob { job := &CloseDecisionsJob{} job.Schedule() return job } func (j *CloseDecisionsJob) Schedule() { var nextDue *time.Time nextDue, err := GetNextPendingDecisionDue() if err != nil { log.Error("Could not get next pending due date") if j.timer != nil { j.timer.Stop() j.timer = nil } return } if nextDue == nil { log.Info("no next planned execution of CloseDecisionsJob") j.Stop() } else { nextDue := nextDue.Add(time.Second) log.Infof("scheduling CloseDecisionsJob for %s", nextDue) when := nextDue.Sub(time.Now()) if j.timer != nil { j.timer.Reset(when) } else { j.timer = time.AfterFunc(when, j.Run) } } } func (j *CloseDecisionsJob) Stop() { if j.timer != nil { j.timer.Stop() j.timer = nil } } func (j *CloseDecisionsJob) Run() { log.Debug("running CloseDecisionsJob") err := CloseDecisions() if err != nil { log.Errorf("closing decisions %v", err) } rescheduleChannel <- JobIdCloseDecisions } func (j *CloseDecisionsJob) String() string { return "CloseDecisionsJob" } type RemindVotersJob struct { timer *time.Timer } func NewRemindVotersJob() *RemindVotersJob { job := &RemindVotersJob{} job.Schedule() return job } func (j *RemindVotersJob) Schedule() { year, month, day := time.Now().UTC().Date() nextExecution := time.Date(year, month, day, 0, 0, 0, 0, time.UTC).AddDate(0, 0, 3) log.Infof("scheduling RemindVotersJob for %s", nextExecution) when := nextExecution.Sub(time.Now()) if j.timer != nil { j.timer.Reset(when) } else { j.timer = time.AfterFunc(when, j.Run) } } func (j *RemindVotersJob) Stop() { if j.timer != nil { j.timer.Stop() j.timer = nil } } func (j *RemindVotersJob) Run() { log.Info("running RemindVotersJob") defer func() { rescheduleChannel <- JobIdRemindVotersJob }() voters, err := GetReminderVoters() if err != nil { log.Errorf("problem getting voters %v", err) return } for _, voter := range *voters { decisions, err := FindUnvotedDecisionsForVoter(&voter) if err != nil { log.Errorf("problem getting unvoted decisions: %v", err) return } if len(*decisions) > 0 { NotifyMailChannel <- &RemindVoterNotification{voter: voter, decisions: *decisions} } } }