github.com/levb/mattermost-server@v5.3.1+incompatible/migrations/worker.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 "context" 8 "net/http" 9 "time" 10 11 "github.com/mattermost/mattermost-server/app" 12 "github.com/mattermost/mattermost-server/jobs" 13 "github.com/mattermost/mattermost-server/mlog" 14 "github.com/mattermost/mattermost-server/model" 15 ) 16 17 const ( 18 TIME_BETWEEN_BATCHES = 100 19 ) 20 21 type Worker struct { 22 name string 23 stop chan bool 24 stopped chan bool 25 jobs chan model.Job 26 jobServer *jobs.JobServer 27 app *app.App 28 } 29 30 func (m *MigrationsJobInterfaceImpl) MakeWorker() model.Worker { 31 worker := Worker{ 32 name: "Migrations", 33 stop: make(chan bool, 1), 34 stopped: make(chan bool, 1), 35 jobs: make(chan model.Job), 36 jobServer: m.App.Jobs, 37 app: m.App, 38 } 39 40 return &worker 41 } 42 43 func (worker *Worker) Run() { 44 mlog.Debug("Worker started", mlog.String("worker", worker.name)) 45 46 defer func() { 47 mlog.Debug("Worker finished", mlog.String("worker", worker.name)) 48 worker.stopped <- true 49 }() 50 51 for { 52 select { 53 case <-worker.stop: 54 mlog.Debug("Worker received stop signal", mlog.String("worker", worker.name)) 55 return 56 case job := <-worker.jobs: 57 mlog.Debug("Worker received a new candidate job.", mlog.String("worker", worker.name)) 58 worker.DoJob(&job) 59 } 60 } 61 } 62 63 func (worker *Worker) Stop() { 64 mlog.Debug("Worker stopping", mlog.String("worker", worker.name)) 65 worker.stop <- true 66 <-worker.stopped 67 } 68 69 func (worker *Worker) JobChannel() chan<- model.Job { 70 return worker.jobs 71 } 72 73 func (worker *Worker) DoJob(job *model.Job) { 74 if claimed, err := worker.jobServer.ClaimJob(job); err != nil { 75 mlog.Info("Worker experienced an error while trying to claim job", 76 mlog.String("worker", worker.name), 77 mlog.String("job_id", job.Id), 78 mlog.String("error", err.Error())) 79 return 80 } else if !claimed { 81 return 82 } 83 84 cancelCtx, cancelCancelWatcher := context.WithCancel(context.Background()) 85 cancelWatcherChan := make(chan interface{}, 1) 86 go worker.app.Jobs.CancellationWatcher(cancelCtx, job.Id, cancelWatcherChan) 87 88 defer cancelCancelWatcher() 89 90 for { 91 select { 92 case <-cancelWatcherChan: 93 mlog.Debug("Worker: Job has been canceled via CancellationWatcher", mlog.String("worker", worker.name), mlog.String("job_id", job.Id)) 94 worker.setJobCanceled(job) 95 return 96 97 case <-worker.stop: 98 mlog.Debug("Worker: Job has been canceled via Worker Stop", mlog.String("worker", worker.name), mlog.String("job_id", job.Id)) 99 worker.setJobCanceled(job) 100 return 101 102 case <-time.After(TIME_BETWEEN_BATCHES * time.Millisecond): 103 done, progress, err := worker.runMigration(job.Data[JOB_DATA_KEY_MIGRATION], job.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE]) 104 if err != nil { 105 mlog.Error("Worker: Failed to run migration", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 106 worker.setJobError(job, err) 107 return 108 } else if done { 109 mlog.Info("Worker: Job is complete", mlog.String("worker", worker.name), mlog.String("job_id", job.Id)) 110 worker.setJobSuccess(job) 111 return 112 } else { 113 job.Data[JOB_DATA_KEY_MIGRATION_LAST_DONE] = progress 114 if err := worker.app.Jobs.UpdateInProgressJobData(job); err != nil { 115 mlog.Error("Worker: Failed to update migration status data for job", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 116 worker.setJobError(job, err) 117 return 118 } 119 } 120 } 121 } 122 } 123 124 func (worker *Worker) setJobSuccess(job *model.Job) { 125 if err := worker.app.Jobs.SetJobSuccess(job); err != nil { 126 mlog.Error("Worker: Failed to set success for job", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 127 worker.setJobError(job, err) 128 } 129 } 130 131 func (worker *Worker) setJobError(job *model.Job, appError *model.AppError) { 132 if err := worker.app.Jobs.SetJobError(job, appError); err != nil { 133 mlog.Error("Worker: Failed to set job error", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 134 } 135 } 136 137 func (worker *Worker) setJobCanceled(job *model.Job) { 138 if err := worker.app.Jobs.SetJobCanceled(job); err != nil { 139 mlog.Error("Worker: Failed to mark job as canceled", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error())) 140 } 141 } 142 143 // Return parameters: 144 // - whether the migration is completed on this run (true) or still incomplete (false). 145 // - the updated lastDone string for the migration. 146 // - any error which may have occurred while running the migration. 147 func (worker *Worker) runMigration(key string, lastDone string) (bool, string, *model.AppError) { 148 var done bool 149 var progress string 150 var err *model.AppError 151 152 switch key { 153 case model.MIGRATION_KEY_ADVANCED_PERMISSIONS_PHASE_2: 154 done, progress, err = worker.runAdvancedPermissionsPhase2Migration(lastDone) 155 default: 156 return false, "", model.NewAppError("MigrationsWorker.runMigration", "migrations.worker.run_migration.unknown_key", map[string]interface{}{"key": key}, "", http.StatusInternalServerError) 157 } 158 159 if done { 160 if result := <-worker.app.Srv.Store.System().Save(&model.System{Name: key, Value: "true"}); result.Err != nil { 161 return false, "", result.Err 162 } 163 } 164 165 return done, progress, err 166 }