github.com/levb/mattermost-server@v5.3.1+incompatible/migrations/scheduler.go (about) 1 // Copyright (c) 2018-present Mattermost, Inc. All Rights Reserved. 2 // See License.txt for license information. 3 4 package migrations 5 6 import ( 7 "time" 8 9 "github.com/mattermost/mattermost-server/app" 10 "github.com/mattermost/mattermost-server/mlog" 11 "github.com/mattermost/mattermost-server/model" 12 "github.com/mattermost/mattermost-server/store" 13 ) 14 15 const ( 16 MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS = 3600000 // 1 hour 17 ) 18 19 type Scheduler struct { 20 App *app.App 21 allMigrationsCompleted bool 22 } 23 24 func (m *MigrationsJobInterfaceImpl) MakeScheduler() model.Scheduler { 25 return &Scheduler{m.App, false} 26 } 27 28 func (scheduler *Scheduler) Name() string { 29 return "MigrationsScheduler" 30 } 31 32 func (scheduler *Scheduler) JobType() string { 33 return model.JOB_TYPE_MIGRATIONS 34 } 35 36 func (scheduler *Scheduler) Enabled(cfg *model.Config) bool { 37 return true 38 } 39 40 func (scheduler *Scheduler) NextScheduleTime(cfg *model.Config, now time.Time, pendingJobs bool, lastSuccessfulJob *model.Job) *time.Time { 41 if scheduler.allMigrationsCompleted { 42 return nil 43 } 44 45 nextTime := time.Now().Add(60 * time.Second) 46 return &nextTime 47 } 48 49 func (scheduler *Scheduler) ScheduleJob(cfg *model.Config, pendingJobs bool, lastSuccessfulJob *model.Job) (*model.Job, *model.AppError) { 50 mlog.Debug("Scheduling Job", mlog.String("scheduler", scheduler.Name())) 51 52 // Work through the list of migrations in order. Schedule the first one that isn't done (assuming it isn't in progress already). 53 for _, key := range MakeMigrationsList() { 54 state, job, err := GetMigrationState(key, scheduler.App.Srv.Store) 55 if err != nil { 56 mlog.Error("Failed to determine status of migration: ", mlog.String("scheduler", scheduler.Name()), mlog.String("migration_key", key), mlog.String("error", err.Error())) 57 return nil, nil 58 } 59 60 if state == MIGRATION_STATE_IN_PROGRESS { 61 // Check the migration job isn't wedged. 62 if job != nil && job.LastActivityAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS && job.CreateAt < model.GetMillis()-MIGRATION_JOB_WEDGED_TIMEOUT_MILLISECONDS { 63 mlog.Warn("Job appears to be wedged. Rescheduling another instance.", mlog.String("scheduler", scheduler.Name()), mlog.String("wedged_job_id", job.Id), mlog.String("migration_key", key)) 64 if err := scheduler.App.Jobs.SetJobError(job, nil); err != nil { 65 mlog.Error("Worker: Failed to set job error", mlog.String("scheduler", scheduler.Name()), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 66 } 67 return scheduler.createJob(key, job, scheduler.App.Srv.Store) 68 } 69 70 return nil, nil 71 } 72 73 if state == MIGRATION_STATE_COMPLETED { 74 // This migration is done. Continue to check the next. 75 continue 76 } 77 78 if state == MIGRATION_STATE_UNSCHEDULED { 79 mlog.Debug("Scheduling a new job for migration.", mlog.String("scheduler", scheduler.Name()), mlog.String("migration_key", key)) 80 return scheduler.createJob(key, job, scheduler.App.Srv.Store) 81 } 82 83 mlog.Error("Unknown migration state. Not doing anything.", mlog.String("migration_state", state)) 84 return nil, nil 85 } 86 87 // If we reached here, then there aren't any migrations left to run. 88 scheduler.allMigrationsCompleted = true 89 mlog.Debug("All migrations are complete.", mlog.String("scheduler", scheduler.Name())) 90 91 return nil, nil 92 } 93 94 func (scheduler *Scheduler) createJob(migrationKey string, lastJob *model.Job, store store.Store) (*model.Job, *model.AppError) { 95 var lastDone string 96 if lastJob != nil { 97 lastDone = lastJob.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE] 98 } 99 100 data := map[string]string{ 101 JOB_DATA_KEY_MIGRATION: migrationKey, 102 JOB_DATA_KEY_MIGRATION_LAST_DONE: lastDone, 103 } 104 105 if job, err := scheduler.App.Jobs.CreateJob(model.JOB_TYPE_MIGRATIONS, data); err != nil { 106 return nil, err 107 } else { 108 return job, nil 109 } 110 }