Skip to content
/ gojm Public

A thread-safe and reliable priority-based job scheduling manager

License

Notifications You must be signed in to change notification settings

xybor-x/gojm

Repository files navigation

xybor founder Go Reference GitHub Repo stars GitHub top language GitHub go.mod Go version GitHub release (release name instead of tag name) Codacy Badge Codacy Badge Go Report

Introduction

A thread-safe and reliable priority-based job manager.

Job

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)

Job result

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

Job manager

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

Hook

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)
    }
})

About

A thread-safe and reliable priority-based job scheduling manager

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages