github.com/spreadshirt/mattermost-server@v5.3.2-0.20180927191755-a257d501df3d+incompatible/jobs/schedulers.go (about) 1 // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package jobs 5 6 import ( 7 "fmt" 8 "sync" 9 "time" 10 11 "github.com/mattermost/mattermost-server/mlog" 12 "github.com/mattermost/mattermost-server/model" 13 ) 14 15 type Schedulers struct { 16 stop chan bool 17 stopped chan bool 18 configChanged chan *model.Config 19 clusterLeaderChanged chan bool 20 listenerId string 21 startOnce sync.Once 22 jobs *JobServer 23 24 schedulers []model.Scheduler 25 nextRunTimes []*time.Time 26 } 27 28 func (srv *JobServer) InitSchedulers() *Schedulers { 29 mlog.Debug("Initialising schedulers.") 30 31 schedulers := &Schedulers{ 32 stop: make(chan bool), 33 stopped: make(chan bool), 34 configChanged: make(chan *model.Config), 35 clusterLeaderChanged: make(chan bool), 36 jobs: srv, 37 } 38 39 if srv.DataRetentionJob != nil { 40 schedulers.schedulers = append(schedulers.schedulers, srv.DataRetentionJob.MakeScheduler()) 41 } 42 43 if srv.MessageExportJob != nil { 44 schedulers.schedulers = append(schedulers.schedulers, srv.MessageExportJob.MakeScheduler()) 45 } 46 47 if elasticsearchAggregatorInterface := srv.ElasticsearchAggregator; elasticsearchAggregatorInterface != nil { 48 schedulers.schedulers = append(schedulers.schedulers, elasticsearchAggregatorInterface.MakeScheduler()) 49 } 50 51 if ldapSyncInterface := srv.LdapSync; ldapSyncInterface != nil { 52 schedulers.schedulers = append(schedulers.schedulers, ldapSyncInterface.MakeScheduler()) 53 } 54 55 if migrationsInterface := srv.Migrations; migrationsInterface != nil { 56 schedulers.schedulers = append(schedulers.schedulers, migrationsInterface.MakeScheduler()) 57 } 58 59 schedulers.nextRunTimes = make([]*time.Time, len(schedulers.schedulers)) 60 return schedulers 61 } 62 63 func (schedulers *Schedulers) Start() *Schedulers { 64 schedulers.listenerId = schedulers.jobs.ConfigService.AddConfigListener(schedulers.handleConfigChange) 65 66 go func() { 67 schedulers.startOnce.Do(func() { 68 mlog.Info("Starting schedulers.") 69 70 defer func() { 71 mlog.Info("Schedulers stopped.") 72 close(schedulers.stopped) 73 }() 74 75 now := time.Now() 76 for idx, scheduler := range schedulers.schedulers { 77 if !scheduler.Enabled(schedulers.jobs.Config()) { 78 schedulers.nextRunTimes[idx] = nil 79 } else { 80 schedulers.setNextRunTime(schedulers.jobs.Config(), idx, now, false) 81 } 82 } 83 84 for { 85 select { 86 case <-schedulers.stop: 87 mlog.Debug("Schedulers received stop signal.") 88 return 89 case now = <-time.After(1 * time.Minute): 90 cfg := schedulers.jobs.Config() 91 92 for idx, nextTime := range schedulers.nextRunTimes { 93 if nextTime == nil { 94 continue 95 } 96 97 if time.Now().After(*nextTime) { 98 scheduler := schedulers.schedulers[idx] 99 if scheduler != nil { 100 if scheduler.Enabled(cfg) { 101 if _, err := schedulers.scheduleJob(cfg, scheduler); err != nil { 102 mlog.Warn(fmt.Sprintf("Failed to schedule job with scheduler: %v", scheduler.Name())) 103 mlog.Error(fmt.Sprint(err)) 104 } else { 105 schedulers.setNextRunTime(cfg, idx, now, true) 106 } 107 } 108 } 109 } 110 } 111 case newCfg := <-schedulers.configChanged: 112 for idx, scheduler := range schedulers.schedulers { 113 if !scheduler.Enabled(newCfg) { 114 schedulers.nextRunTimes[idx] = nil 115 } else { 116 schedulers.setNextRunTime(newCfg, idx, now, false) 117 } 118 } 119 case isLeader := <-schedulers.clusterLeaderChanged: 120 for idx := range schedulers.schedulers { 121 if !isLeader { 122 schedulers.nextRunTimes[idx] = nil 123 } else { 124 schedulers.setNextRunTime(schedulers.jobs.Config(), idx, now, false) 125 } 126 } 127 } 128 } 129 }) 130 }() 131 132 return schedulers 133 } 134 135 func (schedulers *Schedulers) Stop() *Schedulers { 136 mlog.Info("Stopping schedulers.") 137 close(schedulers.stop) 138 <-schedulers.stopped 139 return schedulers 140 } 141 142 func (schedulers *Schedulers) setNextRunTime(cfg *model.Config, idx int, now time.Time, pendingJobs bool) { 143 scheduler := schedulers.schedulers[idx] 144 145 if !pendingJobs { 146 if pj, err := schedulers.jobs.CheckForPendingJobsByType(scheduler.JobType()); err != nil { 147 mlog.Error("Failed to set next job run time: " + err.Error()) 148 schedulers.nextRunTimes[idx] = nil 149 return 150 } else { 151 pendingJobs = pj 152 } 153 } 154 155 lastSuccessfulJob, err := schedulers.jobs.GetLastSuccessfulJobByType(scheduler.JobType()) 156 if err != nil { 157 mlog.Error("Failed to set next job run time: " + err.Error()) 158 schedulers.nextRunTimes[idx] = nil 159 return 160 } 161 162 schedulers.nextRunTimes[idx] = scheduler.NextScheduleTime(cfg, now, pendingJobs, lastSuccessfulJob) 163 mlog.Debug(fmt.Sprintf("Next run time for scheduler %v: %v", scheduler.Name(), schedulers.nextRunTimes[idx])) 164 } 165 166 func (schedulers *Schedulers) scheduleJob(cfg *model.Config, scheduler model.Scheduler) (*model.Job, *model.AppError) { 167 pendingJobs, err := schedulers.jobs.CheckForPendingJobsByType(scheduler.JobType()) 168 if err != nil { 169 return nil, err 170 } 171 172 lastSuccessfulJob, err2 := schedulers.jobs.GetLastSuccessfulJobByType(scheduler.JobType()) 173 if err2 != nil { 174 return nil, err 175 } 176 177 return scheduler.ScheduleJob(cfg, pendingJobs, lastSuccessfulJob) 178 } 179 180 func (schedulers *Schedulers) handleConfigChange(oldConfig *model.Config, newConfig *model.Config) { 181 mlog.Debug("Schedulers received config change.") 182 schedulers.configChanged <- newConfig 183 } 184 185 func (schedulers *Schedulers) HandleClusterLeaderChange(isLeader bool) { 186 select { 187 case schedulers.clusterLeaderChanged <- isLeader: 188 default: 189 mlog.Debug("Did not send cluster leader change message to schedulers as no schedulers listening to notification channel.") 190 } 191 }