MM-47861 Set paniced jobs to "failed" (#21500)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
0b7dd5b965
Коммит
c194fa3f45
@@ -19,6 +19,8 @@ func MakeWorker(jobServer *jobs.JobServer, store store.Store, getMetrics func()
|
|||||||
return *cfg.MetricsSettings.Enable
|
return *cfg.MetricsSettings.Enable
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
count, err := store.User().Count(model.UserCountOptions{IncludeDeleted: false})
|
count, err := store.User().Count(model.UserCountOptions{IncludeDeleted: false})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ func MakeWorker(jobServer *jobs.JobServer, notifySessionsExpired func() error) m
|
|||||||
return *cfg.ServiceSettings.ExtendSessionLengthWithActivity
|
return *cfg.ServiceSettings.ExtendSessionLengthWithActivity
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
return notifySessionsExpired()
|
return notifySessionsExpired()
|
||||||
}
|
}
|
||||||
return jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
return jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface) model.Worker {
|
|||||||
return *cfg.ExportSettings.Directory != "" && *cfg.ExportSettings.RetentionDays > 0
|
return *cfg.ExportSettings.Directory != "" && *cfg.ExportSettings.RetentionDays > 0
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
exportPath := *app.Config().ExportSettings.Directory
|
exportPath := *app.Config().ExportSettings.Directory
|
||||||
retentionTime := time.Duration(*app.Config().ExportSettings.RetentionDays) * 24 * time.Hour
|
retentionTime := time.Duration(*app.Config().ExportSettings.RetentionDays) * 24 * time.Hour
|
||||||
exports, appErr := app.ListDirectory(exportPath)
|
exports, appErr := app.ListDirectory(exportPath)
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ type AppIface interface {
|
|||||||
func MakeWorker(jobServer *jobs.JobServer, app AppIface) model.Worker {
|
func MakeWorker(jobServer *jobs.JobServer, app AppIface) model.Worker {
|
||||||
isEnabled := func(cfg *model.Config) bool { return true }
|
isEnabled := func(cfg *model.Config) bool { return true }
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
opts := model.BulkExportOpts{
|
opts := model.BulkExportOpts{
|
||||||
CreateArchive: true,
|
CreateArchive: true,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface, store store.Store) mode
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var fromTS int64 = 0
|
var fromTS int64 = 0
|
||||||
var toTS int64 = model.GetMillis()
|
var toTS int64 = model.GetMillis()
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface, s store.Store) model.Wo
|
|||||||
return *cfg.ImportSettings.Directory != "" && *cfg.ImportSettings.RetentionDays > 0
|
return *cfg.ImportSettings.Directory != "" && *cfg.ImportSettings.RetentionDays > 0
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
importPath := *app.Config().ImportSettings.Directory
|
importPath := *app.Config().ImportSettings.Directory
|
||||||
retentionTime := time.Duration(*app.Config().ImportSettings.RetentionDays) * 24 * time.Hour
|
retentionTime := time.Duration(*app.Config().ImportSettings.RetentionDays) * 24 * time.Hour
|
||||||
imports, appErr := app.ListDirectory(importPath)
|
imports, appErr := app.ListDirectory(importPath)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface) model.Worker {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
importFileName, ok := job.Data["import_file"]
|
importFileName, ok := job.Data["import_file"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return model.NewAppError("ImportProcessWorker", "import_process.worker.do_job.missing_file", nil, "", http.StatusBadRequest)
|
return model.NewAppError("ImportProcessWorker", "import_process.worker.do_job.missing_file", nil, "", http.StatusBadRequest)
|
||||||
|
|||||||
28
jobs/jobs.go
28
jobs/jobs.go
@@ -6,7 +6,10 @@ package jobs
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"runtime/pprof"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v6/model"
|
"github.com/mattermost/mattermost-server/v6/model"
|
||||||
@@ -179,6 +182,31 @@ func (srv *JobServer) UpdateInProgressJobData(job *model.Job) *model.AppError {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HandleJobPanic is used to handle panics during the execution of a job. It logs the panic and sets the status for the job.
|
||||||
|
// After handling, the method repanics! This method is supposed to be `defer`'d at the start of the job.
|
||||||
|
func (srv *JobServer) HandleJobPanic(job *model.Job) {
|
||||||
|
r := recover()
|
||||||
|
if r == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sb := &strings.Builder{}
|
||||||
|
pprof.Lookup("goroutine").WriteTo(sb, 2)
|
||||||
|
mlog.Error("Unhandled panic in job", mlog.Any("panic", r), mlog.Any("job", job), mlog.String("stack", sb.String()))
|
||||||
|
|
||||||
|
rerr, ok := r.(error)
|
||||||
|
if !ok {
|
||||||
|
rerr = fmt.Errorf("job panic: %v", r)
|
||||||
|
}
|
||||||
|
|
||||||
|
appErr := srv.SetJobError(job, model.NewAppError("HandleJobPanic", "app.job.update.app_error", nil, "", http.StatusInternalServerError)).Wrap(rerr)
|
||||||
|
if appErr != nil {
|
||||||
|
mlog.Error("Failed to set the job status to 'failed'", mlog.Err(appErr), mlog.Any("job", job))
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(r)
|
||||||
|
}
|
||||||
|
|
||||||
func (srv *JobServer) RequestCancellation(jobId string) *model.AppError {
|
func (srv *JobServer) RequestCancellation(jobId string) *model.AppError {
|
||||||
updated, err := srv.Store.Job().UpdateStatusOptimistically(jobId, model.JobStatusPending, model.JobStatusCanceled)
|
updated, err := srv.Store.Job().UpdateStatusOptimistically(jobId, model.JobStatusPending, model.JobStatusCanceled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package jobs
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -494,6 +495,65 @@ func TestUpdateInProgressJobData(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHandleJobPanic(t *testing.T) {
|
||||||
|
t.Run("no panic", func(t *testing.T) {
|
||||||
|
jobServer, _, _ := makeJobServer(t)
|
||||||
|
|
||||||
|
job := &model.Job{
|
||||||
|
Type: model.JobTypeImportProcess,
|
||||||
|
Status: model.JobStatusInProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
f := func() {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
fmt.Println("OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotPanics(t, f)
|
||||||
|
require.Equal(t, model.JobStatusInProgress, job.Status)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with panic string", func(t *testing.T) {
|
||||||
|
jobServer, mockStore, metrics := makeJobServer(t)
|
||||||
|
|
||||||
|
job := &model.Job{
|
||||||
|
Type: model.JobTypeImportProcess,
|
||||||
|
Status: model.JobStatusInProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
f := func() {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
panic("not OK")
|
||||||
|
}
|
||||||
|
|
||||||
|
mockStore.JobStore.On("UpdateOptimistically", job, model.JobStatusInProgress).Return(true, nil)
|
||||||
|
metrics.On("DecrementJobActive", model.JobTypeImportProcess)
|
||||||
|
|
||||||
|
require.Panics(t, f)
|
||||||
|
require.Equal(t, model.JobStatusError, job.Status)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("with panic error", func(t *testing.T) {
|
||||||
|
jobServer, mockStore, metrics := makeJobServer(t)
|
||||||
|
|
||||||
|
job := &model.Job{
|
||||||
|
Type: model.JobTypeImportProcess,
|
||||||
|
Status: model.JobStatusInProgress,
|
||||||
|
}
|
||||||
|
|
||||||
|
f := func() {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
panic(fmt.Errorf("not OK"))
|
||||||
|
}
|
||||||
|
|
||||||
|
mockStore.JobStore.On("UpdateOptimistically", job, model.JobStatusInProgress).Return(true, nil)
|
||||||
|
metrics.On("DecrementJobActive", model.JobTypeImportProcess)
|
||||||
|
|
||||||
|
require.Panics(t, f)
|
||||||
|
require.Equal(t, model.JobStatusError, job.Status)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestRequestCancellation(t *testing.T) {
|
func TestRequestCancellation(t *testing.T) {
|
||||||
t.Run("error cancelling", func(t *testing.T) {
|
t.Run("error cancelling", func(t *testing.T) {
|
||||||
jobServer, mockStore, _ := makeJobServer(t)
|
jobServer, mockStore, _ := makeJobServer(t)
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ func MakeWorker(jobServer *jobs.JobServer, license *model.License, app AppIface)
|
|||||||
isEnabled := func(_ *model.Config) bool {
|
isEnabled := func(_ *model.Config) bool {
|
||||||
return license != nil && *license.Features.Cloud
|
return license != nil && *license.Features.Cloud
|
||||||
}
|
}
|
||||||
execute := func(_ *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
return app.ComputeLastAccessibleFileTime()
|
return app.ComputeLastAccessibleFileTime()
|
||||||
}
|
}
|
||||||
worker := jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
worker := jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ func MakeWorker(jobServer *jobs.JobServer, license *model.License, app AppIface)
|
|||||||
isEnabled := func(_ *model.Config) bool {
|
isEnabled := func(_ *model.Config) bool {
|
||||||
return license != nil && license.Features != nil && *license.Features.Cloud
|
return license != nil && license.Features != nil && *license.Features.Cloud
|
||||||
}
|
}
|
||||||
execute := func(_ *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
return app.ComputeLastAccessiblePostTime()
|
return app.ComputeLastAccessiblePostTime()
|
||||||
}
|
}
|
||||||
worker := jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
worker := jobs.NewSimpleWorker(JobName, jobServer, execute, isEnabled)
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ func (worker *Worker) IsEnabled(_ *model.Config) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (worker *Worker) DoJob(job *model.Job) {
|
func (worker *Worker) DoJob(job *model.Job) {
|
||||||
|
defer worker.jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
if claimed, err := worker.jobServer.ClaimJob(job); err != nil {
|
if claimed, err := worker.jobServer.ClaimJob(job); err != nil {
|
||||||
mlog.Info("Worker experienced an error while trying to claim job",
|
mlog.Info("Worker experienced an error while trying to claim job",
|
||||||
mlog.String("worker", worker.name),
|
mlog.String("worker", worker.name),
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ func MakeUpgradeNotifyWorker(jobServer *jobs.JobServer, license *model.License,
|
|||||||
isEnabled := func(_ *model.Config) bool {
|
isEnabled := func(_ *model.Config) bool {
|
||||||
return license != nil && license.Features != nil && *license.Features.Cloud
|
return license != nil && license.Features != nil && *license.Features.Cloud
|
||||||
}
|
}
|
||||||
execute := func(_ *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
appErr := app.DoCheckForAdminNotifications(false)
|
appErr := app.DoCheckForAdminNotifications(false)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
@@ -37,7 +39,9 @@ func MakeTrialNotifyWorker(jobServer *jobs.JobServer, license *model.License, ap
|
|||||||
isEnabled := func(_ *model.Config) bool {
|
isEnabled := func(_ *model.Config) bool {
|
||||||
return license != nil && license.Features != nil && *license.Features.Cloud
|
return license != nil && license.Features != nil && *license.Features.Cloud
|
||||||
}
|
}
|
||||||
execute := func(_ *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
appErr := app.DoCheckForAdminNotifications(true)
|
appErr := app.DoCheckForAdminNotifications(true)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
return appErr
|
return appErr
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ func MakeWorker(jobServer *jobs.JobServer, app AppIface) model.Worker {
|
|||||||
return *cfg.AnnouncementSettings.AdminNoticesEnabled || *cfg.AnnouncementSettings.UserNoticesEnabled
|
return *cfg.AnnouncementSettings.AdminNoticesEnabled || *cfg.AnnouncementSettings.UserNoticesEnabled
|
||||||
}
|
}
|
||||||
execute := func(job *model.Job) error {
|
execute := func(job *model.Job) error {
|
||||||
|
defer jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
if err := app.UpdateProductNotices(); err != nil {
|
if err := app.UpdateProductNotices(); err != nil {
|
||||||
mlog.Error("Worker: Failed to fetch product notices", mlog.String("worker", model.JobTypeProductNotices), mlog.String("job_id", job.Id), mlog.Err(err))
|
mlog.Error("Worker: Failed to fetch product notices", mlog.String("worker", model.JobTypeProductNotices), mlog.String("job_id", job.Id), mlog.Err(err))
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ func (rseworker *ResendInvitationEmailWorker) JobChannel() chan<- model.Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rseworker *ResendInvitationEmailWorker) DoJob(job *model.Job) {
|
func (rseworker *ResendInvitationEmailWorker) DoJob(job *model.Job) {
|
||||||
|
defer rseworker.jobServer.HandleJobPanic(job)
|
||||||
|
|
||||||
elapsedTimeSinceSchedule, DurationInMillis := rseworker.GetDurations(job)
|
elapsedTimeSinceSchedule, DurationInMillis := rseworker.GetDurations(job)
|
||||||
if elapsedTimeSinceSchedule > DurationInMillis {
|
if elapsedTimeSinceSchedule > DurationInMillis {
|
||||||
rseworker.ResendEmails(job, "48")
|
rseworker.ResendEmails(job, "48")
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user