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  }