github.com/adacta-ru/mattermost-server/v5@v5.31.1/jobs/schedulers.go (about)

     1  // Copyright (c) 2015-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/adacta-ru/mattermost-server/v5/mlog"
    12  	"github.com/adacta-ru/mattermost-server/v5/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  	isLeader             bool
    24  
    25  	schedulers   []model.Scheduler
    26  	nextRunTimes []*time.Time
    27  }
    28  
    29  func (srv *JobServer) InitSchedulers() *Schedulers {
    30  	mlog.Debug("Initialising schedulers.")
    31  
    32  	schedulers := &Schedulers{
    33  		stop:                 make(chan bool),
    34  		stopped:              make(chan bool),
    35  		configChanged:        make(chan *model.Config),
    36  		clusterLeaderChanged: make(chan bool),
    37  		jobs:                 srv,
    38  		isLeader:             true,
    39  	}
    40  
    41  	if srv.DataRetentionJob != nil {
    42  		schedulers.schedulers = append(schedulers.schedulers, srv.DataRetentionJob.MakeScheduler())
    43  	}
    44  
    45  	if srv.MessageExportJob != nil {
    46  		schedulers.schedulers = append(schedulers.schedulers, srv.MessageExportJob.MakeScheduler())
    47  	}
    48  
    49  	if elasticsearchAggregatorInterface := srv.ElasticsearchAggregator; elasticsearchAggregatorInterface != nil {
    50  		schedulers.schedulers = append(schedulers.schedulers, elasticsearchAggregatorInterface.MakeScheduler())
    51  	}
    52  
    53  	if ldapSyncInterface := srv.LdapSync; ldapSyncInterface != nil {
    54  		schedulers.schedulers = append(schedulers.schedulers, ldapSyncInterface.MakeScheduler())
    55  	}
    56  
    57  	if migrationsInterface := srv.Migrations; migrationsInterface != nil {
    58  		schedulers.schedulers = append(schedulers.schedulers, migrationsInterface.MakeScheduler())
    59  	}
    60  
    61  	if pluginsInterface := srv.Plugins; pluginsInterface != nil {
    62  		schedulers.schedulers = append(schedulers.schedulers, pluginsInterface.MakeScheduler())
    63  	}
    64  
    65  	if expiryNotifyInterface := srv.ExpiryNotify; expiryNotifyInterface != nil {
    66  		schedulers.schedulers = append(schedulers.schedulers, expiryNotifyInterface.MakeScheduler())
    67  	}
    68  
    69  	if activeUsersInterface := srv.ActiveUsers; activeUsersInterface != nil {
    70  		schedulers.schedulers = append(schedulers.schedulers, activeUsersInterface.MakeScheduler())
    71  	}
    72  	if productNoticesInterface := srv.ProductNotices; productNoticesInterface != nil {
    73  		schedulers.schedulers = append(schedulers.schedulers, productNoticesInterface.MakeScheduler())
    74  	}
    75  
    76  	if cloudInterface := srv.Cloud; cloudInterface != nil {
    77  		schedulers.schedulers = append(schedulers.schedulers, cloudInterface.MakeScheduler())
    78  	}
    79  
    80  	schedulers.nextRunTimes = make([]*time.Time, len(schedulers.schedulers))
    81  	return schedulers
    82  }
    83  
    84  func (schedulers *Schedulers) Start() *Schedulers {
    85  	schedulers.listenerId = schedulers.jobs.ConfigService.AddConfigListener(schedulers.handleConfigChange)
    86  
    87  	go func() {
    88  		schedulers.startOnce.Do(func() {
    89  			mlog.Info("Starting schedulers.")
    90  
    91  			defer func() {
    92  				mlog.Info("Schedulers stopped.")
    93  				close(schedulers.stopped)
    94  			}()
    95  
    96  			now := time.Now()
    97  			for idx, scheduler := range schedulers.schedulers {
    98  				if !scheduler.Enabled(schedulers.jobs.Config()) {
    99  					schedulers.nextRunTimes[idx] = nil
   100  				} else {
   101  					schedulers.setNextRunTime(schedulers.jobs.Config(), idx, now, false)
   102  				}
   103  			}
   104  
   105  			for {
   106  				timer := time.NewTimer(1 * time.Minute)
   107  				select {
   108  				case <-schedulers.stop:
   109  					mlog.Debug("Schedulers received stop signal.")
   110  					timer.Stop()
   111  					return
   112  				case now = <-timer.C:
   113  					cfg := schedulers.jobs.Config()
   114  
   115  					for idx, nextTime := range schedulers.nextRunTimes {
   116  						if nextTime == nil {
   117  							continue
   118  						}
   119  
   120  						if time.Now().After(*nextTime) {
   121  							scheduler := schedulers.schedulers[idx]
   122  							if scheduler != nil {
   123  								if schedulers.isLeader && scheduler.Enabled(cfg) {
   124  									if _, err := schedulers.scheduleJob(cfg, scheduler); err != nil {
   125  										mlog.Error("Failed to schedule job", mlog.String("scheduler", scheduler.Name()), mlog.Err(err))
   126  									} else {
   127  										schedulers.setNextRunTime(cfg, idx, now, true)
   128  									}
   129  								}
   130  							}
   131  						}
   132  					}
   133  				case newCfg := <-schedulers.configChanged:
   134  					for idx, scheduler := range schedulers.schedulers {
   135  						if !schedulers.isLeader || !scheduler.Enabled(newCfg) {
   136  							schedulers.nextRunTimes[idx] = nil
   137  						} else {
   138  							schedulers.setNextRunTime(newCfg, idx, now, false)
   139  						}
   140  					}
   141  				case isLeader := <-schedulers.clusterLeaderChanged:
   142  					for idx := range schedulers.schedulers {
   143  						schedulers.isLeader = isLeader
   144  						if !isLeader {
   145  							schedulers.nextRunTimes[idx] = nil
   146  						} else {
   147  							schedulers.setNextRunTime(schedulers.jobs.Config(), idx, now, false)
   148  						}
   149  					}
   150  				}
   151  				timer.Stop()
   152  			}
   153  		})
   154  	}()
   155  
   156  	return schedulers
   157  }
   158  
   159  func (schedulers *Schedulers) Stop() *Schedulers {
   160  	mlog.Info("Stopping schedulers.")
   161  	close(schedulers.stop)
   162  	<-schedulers.stopped
   163  	return schedulers
   164  }
   165  
   166  func (schedulers *Schedulers) setNextRunTime(cfg *model.Config, idx int, now time.Time, pendingJobs bool) {
   167  	scheduler := schedulers.schedulers[idx]
   168  
   169  	if !pendingJobs {
   170  		pj, err := schedulers.jobs.CheckForPendingJobsByType(scheduler.JobType())
   171  		if err != nil {
   172  			mlog.Error("Failed to set next job run time", mlog.Err(err))
   173  			schedulers.nextRunTimes[idx] = nil
   174  			return
   175  		}
   176  		pendingJobs = pj
   177  	}
   178  
   179  	lastSuccessfulJob, err := schedulers.jobs.GetLastSuccessfulJobByType(scheduler.JobType())
   180  	if err != nil {
   181  		mlog.Error("Failed to set next job run time", mlog.Err(err))
   182  		schedulers.nextRunTimes[idx] = nil
   183  		return
   184  	}
   185  
   186  	schedulers.nextRunTimes[idx] = scheduler.NextScheduleTime(cfg, now, pendingJobs, lastSuccessfulJob)
   187  	mlog.Debug("Next run time for scheduler", mlog.String("scheduler_name", scheduler.Name()), mlog.String("next_runtime", fmt.Sprintf("%v", schedulers.nextRunTimes[idx])))
   188  }
   189  
   190  func (schedulers *Schedulers) scheduleJob(cfg *model.Config, scheduler model.Scheduler) (*model.Job, *model.AppError) {
   191  	pendingJobs, err := schedulers.jobs.CheckForPendingJobsByType(scheduler.JobType())
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	lastSuccessfulJob, err2 := schedulers.jobs.GetLastSuccessfulJobByType(scheduler.JobType())
   197  	if err2 != nil {
   198  		return nil, err
   199  	}
   200  
   201  	return scheduler.ScheduleJob(cfg, pendingJobs, lastSuccessfulJob)
   202  }
   203  
   204  func (schedulers *Schedulers) handleConfigChange(oldConfig, newConfig *model.Config) {
   205  	mlog.Debug("Schedulers received config change.")
   206  	schedulers.configChanged <- newConfig
   207  }
   208  
   209  func (schedulers *Schedulers) HandleClusterLeaderChange(isLeader bool) {
   210  	select {
   211  	case schedulers.clusterLeaderChanged <- isLeader:
   212  	default:
   213  		mlog.Debug("Did not send cluster leader change message to schedulers as no schedulers listening to notification channel.")
   214  	}
   215  }