github.com/vnforks/kid/v5@v5.22.1-0.20200408055009-b89d99c65676/migrations/worker.go (about)

     1  // Copyright (c) 2015-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/vnforks/kid/v5/app"
    12  	"github.com/vnforks/kid/v5/jobs"
    13  	"github.com/vnforks/kid/v5/mlog"
    14  	"github.com/vnforks/kid/v5/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.Srv().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) JobClass() 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.Srv().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.Srv().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.Srv().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.Srv().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.Srv().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 saveErr := worker.app.Srv().Store.System().Save(&model.System{Name: key, Value: "true"}); saveErr != nil {
   161  			return false, "", saveErr
   162  		}
   163  	}
   164  
   165  	return done, progress, err
   166  }