A thread-safe and reliable priority-based job manager.
In gojm
, job
is only a wrapper of function. You can define a job as below:
job := gojm.NewJob(function(ctx context.Context) *gojm.JobResult {
fmt.Println("The job started")
time.Sleep(time.Second)
fmt.Println("The job completed")
return nil
})
You can execute the job like you are calling a function. Note that the method
Exec()
should be called only once. The program will panics if you call it
again.
ctx := context.Background()
result := job.Exec(ctx)
You also can get the result with non-blocking mode or blocking mode after the job had already completed. If the job has not completed yet, the result is nil.
// Non-blocking mode
result := job.GetResult()
// Blocking mode
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second) // set timeout as 1 second.
defer cancel()
result := job.WaitResult(ctx)
If you want the job returns a value, you can use JobResult.
job := gojm.NewJob(function(ctx context.Context) *gojm.JobResult {
fmt.Println("The job started")
time.Sleep(time.Second)
fmt.Println("The job completed")
return gojm.Result(100)
})
result := job.Exec(ctx)
fmt.Println(result.Get(nil))
// Output:
// 100
You also can put many values into JobResult.
job := gojm.NewJob(function(ctx context.Context) *gojm.JobResult {
fmt.Println("The job started")
time.Sleep(time.Second)
fmt.Println("The job completed")
result := gojm.EmptyResult()
result.Set("x", 100)
result.Set("y", "abc")
return result
})
result := job.Exec(ctx)
fmt.Println(result.Get("x"), result.Get("y"))
// Output:
// 100 abc
Or return an error.
job := gojm.NewJob(function(ctx context.Context) *gojm.JobResult {
fmt.Println("The job started")
time.Sleep(time.Second)
fmt.Println("The job completed")
return gojm.Err(errors.New("something's wrong"))
})
result := job.Exec(ctx)
fmt.Println(result.Err)
// Output:
// something's wrong
You can put jobs into a job manager with a priority to execute it in when possible.
Firstly, you need to create some Priority
levels. Every Priority
has its own
value, the lower value, the higher priority.
// Urgent is for jobs which need to be executed as soon as possible.
var Urgent = gojm.NewPriority("Urgent", 0)
// Necessary is for jobs which can be executed later but also need to be
// completed soon. We set the aging by one minute (after one minute, this job
// will be moved to the higher priority).
var Necessary = gojm.NewPriority("Necessary", 10).WithAging(time.Minute)
// Background is for jobs which can be completed no matter of time. We must
// specify that we don't need an aging (including default aging) for this
// priority.
var Background = gojm.NewPriority("Background", 1000).WithNoAging()
Start a job manager
jm := gojm.New()
ctx := context.Background()
if err := jm.Run(ctx); err != nil {
panic(err)
}
You can put job into the job manager in another goroutine.
jm.Schedule(Background, gojm.NewJob(func(ctx context.Context) *gojm.JobResult {
fmt.Println("Do background job")
return nil
}))
Hold the job object to wait for its result.
urgentJob := gojm.NewJob(func(ctx context.Context) *gojm.JobResult {
fmt.Println("Do urgent job")
return gojm.Result(0)
})
jm.Schedule(Background, urgentJob)
result := urgentJob.WaitResult(ctx)
fmt.Println(result.Get(nil))
// Output:
// 0
Instead of waiting for the result of each job, you can set a hook function to handle the result of all completed jobs.
jm.Hook(func (ctx context.Context, job gojm.JobWrapper) {
if job.Unwrap().Err != nil {
log.Printf("level=error priority=%s err=%v", job.OriginalPriority, job.Unwrap().Err)
} else if result := job.Unwrap().GetResult().Get(nil); result != nil {
log.Printf("level=info priority=%s result=%v", job.OriginalPriority, result)
}
})