2021-04-03 06:43:16 +02:00
|
|
|
package job
|
2021-04-03 05:51:36 +02:00
|
|
|
|
|
|
|
import (
|
2021-04-24 23:39:23 +08:00
|
|
|
"context"
|
2021-04-03 05:51:36 +02:00
|
|
|
"fmt"
|
2021-04-03 06:43:16 +02:00
|
|
|
"sync"
|
2021-04-03 05:51:36 +02:00
|
|
|
"time"
|
2021-04-10 11:07:02 +02:00
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2021-04-03 05:51:36 +02:00
|
|
|
)
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//Job Wraps JobFun and provide:
|
|
|
|
// 1. Creation, Start, and Finish Time
|
|
|
|
// 2. Recover From Panics
|
2021-04-03 05:51:36 +02:00
|
|
|
type Job struct {
|
|
|
|
id string
|
2021-04-24 23:39:23 +08:00
|
|
|
jobFunc func(ctx context.Context)
|
2021-04-03 05:51:36 +02:00
|
|
|
createTime time.Time
|
|
|
|
startTime time.Time
|
|
|
|
finishTime time.Time
|
|
|
|
state State
|
2021-04-03 06:43:16 +02:00
|
|
|
mx sync.RWMutex
|
2021-04-24 23:39:23 +08:00
|
|
|
ctx context.Context
|
2021-04-03 06:43:16 +02:00
|
|
|
}
|
|
|
|
|
2021-04-24 23:39:23 +08:00
|
|
|
type JobCtxValue struct {}
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//State Return Job current state.
|
2021-04-03 06:43:16 +02:00
|
|
|
func (j *Job) State() State {
|
2021-04-10 12:19:32 +02:00
|
|
|
j.mx.RLock()
|
|
|
|
defer j.mx.RUnlock()
|
2021-04-03 06:43:16 +02:00
|
|
|
return j.state
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//NewJobWithID Create new Job with the supplied Id.
|
2021-04-24 23:39:23 +08:00
|
|
|
func NewJobWithID(ctx context.Context, id string, jobFunc func(context.Context)) *Job {
|
2021-04-03 05:51:36 +02:00
|
|
|
return &Job{
|
|
|
|
id: id,
|
|
|
|
jobFunc: jobFunc,
|
|
|
|
createTime: time.Now(),
|
|
|
|
startTime: time.Time{},
|
|
|
|
finishTime: time.Time{},
|
|
|
|
state: NEW,
|
2021-04-24 23:39:23 +08:00
|
|
|
ctx: ctx,
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-10 15:07:11 +02:00
|
|
|
//NewJob Create new Job, id is assigned a UUID instead.
|
2021-04-24 23:39:23 +08:00
|
|
|
func NewJob(ctx context.Context, jobFunc func(context.Context)) *Job {
|
|
|
|
return NewJobWithID(ctx, uuid.New().String(), jobFunc)
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//ID Return Job ID
|
2021-04-03 05:51:36 +02:00
|
|
|
func (j *Job) ID() string {
|
|
|
|
return j.id
|
|
|
|
}
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//ActualElapsed Return the actual time of procession of Job.
|
|
|
|
// Return -1 if job hasn't started yet.
|
2021-04-10 04:59:43 +02:00
|
|
|
func (j *Job) ActualElapsed() time.Duration {
|
2021-04-03 06:43:16 +02:00
|
|
|
j.mx.RLock()
|
|
|
|
defer j.mx.RUnlock()
|
|
|
|
|
2021-04-03 05:51:36 +02:00
|
|
|
if !j.startTime.IsZero() {
|
|
|
|
if j.finishTime.IsZero() {
|
2021-04-03 06:43:16 +02:00
|
|
|
return time.Since(j.startTime)
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
2021-04-03 06:43:16 +02:00
|
|
|
return j.finishTime.Sub(j.startTime)
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
2021-04-03 06:43:16 +02:00
|
|
|
return -1
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-10 12:19:32 +02:00
|
|
|
//TotalElapsed Returns the total time between creation of object and finishing processing its job.
|
|
|
|
// Return -1 if job hasn't started yet.
|
2021-04-10 04:59:43 +02:00
|
|
|
func (j *Job) TotalElapsed() time.Duration {
|
|
|
|
j.mx.RLock()
|
|
|
|
defer j.mx.RUnlock()
|
|
|
|
|
|
|
|
if !j.startTime.IsZero() {
|
|
|
|
if j.finishTime.IsZero() {
|
|
|
|
return time.Since(j.createTime)
|
|
|
|
}
|
|
|
|
return j.finishTime.Sub(j.createTime)
|
|
|
|
}
|
|
|
|
return -1
|
|
|
|
}
|
2021-04-10 12:19:32 +02:00
|
|
|
|
|
|
|
//Run Run the internal Job (synchronous)
|
2021-04-04 12:51:14 +02:00
|
|
|
func (j *Job) Run() error {
|
|
|
|
return j.run()
|
2021-04-03 05:51:36 +02:00
|
|
|
}
|
|
|
|
|
2021-04-24 23:39:23 +08:00
|
|
|
|
|
|
|
|
2021-04-04 12:51:14 +02:00
|
|
|
func (j *Job) run() (err error) {
|
2021-04-05 11:13:21 +02:00
|
|
|
j.mx.Lock()
|
2021-04-03 06:43:16 +02:00
|
|
|
if j.state != NEW {
|
2021-04-03 08:43:50 +02:00
|
|
|
if j.state == RUNNING {
|
2021-04-03 06:43:16 +02:00
|
|
|
err = ErrorJobStarted{Message: "job already started"}
|
2021-04-05 11:13:21 +02:00
|
|
|
} else {
|
|
|
|
err = ErrorJobStarted{Message: "job finished execution"}
|
2021-04-03 06:43:16 +02:00
|
|
|
}
|
2021-04-05 11:13:21 +02:00
|
|
|
j.mx.Unlock()
|
2021-04-03 06:43:16 +02:00
|
|
|
return err
|
|
|
|
}
|
2021-04-03 05:51:36 +02:00
|
|
|
|
|
|
|
// Handle Panics and set correct state
|
|
|
|
defer func() {
|
2021-04-03 06:43:16 +02:00
|
|
|
j.mx.Lock()
|
2021-04-10 10:35:38 +02:00
|
|
|
// TODO handle panics
|
2021-04-03 05:51:36 +02:00
|
|
|
if r := recover(); r != nil {
|
2021-04-03 06:43:16 +02:00
|
|
|
err = ErrorJobPanic{Message: fmt.Sprintf("job panicked: %v", r)}
|
2021-04-03 05:51:36 +02:00
|
|
|
j.state = PANICKED
|
|
|
|
} else {
|
|
|
|
j.state = FINISHED
|
|
|
|
}
|
|
|
|
j.finishTime = time.Now()
|
2021-04-03 06:43:16 +02:00
|
|
|
j.mx.Unlock()
|
2021-04-03 05:51:36 +02:00
|
|
|
}()
|
|
|
|
|
2021-04-05 11:13:21 +02:00
|
|
|
j.state = RUNNING
|
|
|
|
j.startTime = time.Now()
|
|
|
|
|
2021-04-03 06:43:16 +02:00
|
|
|
// Unlock State
|
|
|
|
j.mx.Unlock()
|
|
|
|
|
2021-04-04 12:51:14 +02:00
|
|
|
// Run Job
|
2021-04-24 23:39:23 +08:00
|
|
|
j.jobFunc(context.WithValue(j.ctx, JobCtxValue{}, j))
|
2021-04-03 05:51:36 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|