This repository has been archived by the owner on Feb 24, 2024. It is now read-only.
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #420 from gobuffalo/workers
Add support for background jobs #95
- Loading branch information
Showing
10 changed files
with
294 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,14 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
|
||
"{{ .actionsPath }}" | ||
"github.com/gobuffalo/envy" | ||
"{{ .actionsPath }}" | ||
"github.com/gobuffalo/envy" | ||
) | ||
|
||
func main() { | ||
port := envy.Get("PORT", "3000") | ||
log.Printf("Starting {{.name}} on port %s\n", port) | ||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), actions.App())) | ||
app := actions.App() | ||
log.Fatal(app.Start(port)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package worker | ||
|
||
// Args are the arguments passed into a job | ||
type Args map[string]interface{} | ||
|
||
// Job to be processed by a Worker | ||
type Job struct { | ||
// Queue the job should be placed into | ||
Queue string | ||
// Args that will be passed to the Handler when run | ||
Args Args | ||
// Handler that will be run by the worker | ||
Handler string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package worker | ||
|
||
import ( | ||
"context" | ||
"sync" | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
) | ||
|
||
var _ Worker = &simple{} | ||
|
||
// NewSimple creates a basic implementation of the Worker interface | ||
// that is backed using just the standard library and goroutines. | ||
func NewSimple() Worker { | ||
return NewSimpleWithContext(context.Background()) | ||
} | ||
|
||
// NewSimpleWithContext creates a basic implementation of the Worker interface | ||
// that is backed using just the standard library and goroutines. | ||
func NewSimpleWithContext(ctx context.Context) Worker { | ||
ctx, cancel := context.WithCancel(ctx) | ||
return &simple{ | ||
ctx: ctx, | ||
cancel: cancel, | ||
handlers: map[string]Handler{}, | ||
moot: &sync.Mutex{}, | ||
} | ||
} | ||
|
||
// simple is a basic implementation of the Worker interface | ||
// that is backed using just the standard library and goroutines. | ||
type simple struct { | ||
ctx context.Context | ||
cancel context.CancelFunc | ||
handlers map[string]Handler | ||
moot *sync.Mutex | ||
} | ||
|
||
func (w *simple) Register(name string, h Handler) error { | ||
w.moot.Lock() | ||
defer w.moot.Unlock() | ||
if _, ok := w.handlers[name]; ok { | ||
return errors.Errorf("handler already mapped for name %s", name) | ||
} | ||
w.handlers[name] = h | ||
return nil | ||
} | ||
|
||
func (w *simple) Start(ctx context.Context) error { | ||
w.ctx, w.cancel = context.WithCancel(ctx) | ||
return nil | ||
} | ||
|
||
func (w simple) Stop() error { | ||
w.cancel() | ||
return nil | ||
} | ||
|
||
// Perform a job as soon as possibly using a goroutine. | ||
func (w simple) Perform(job Job) error { | ||
w.moot.Lock() | ||
defer w.moot.Unlock() | ||
if h, ok := w.handlers[job.Handler]; ok { | ||
go h(job.Args) | ||
return nil | ||
} | ||
return errors.Errorf("no handler mapped for name %s", job.Handler) | ||
} | ||
|
||
// PerformAt performs a job at a particular time using a goroutine. | ||
func (w simple) PerformAt(job Job, t time.Time) error { | ||
return w.PerformIn(job, t.Sub(time.Now())) | ||
} | ||
|
||
// PerformIn performs a job after waiting for a specified amount | ||
// using a goroutine. | ||
func (w simple) PerformIn(job Job, d time.Duration) error { | ||
go func() { | ||
select { | ||
case <-time.After(d): | ||
w.Perform(job) | ||
case <-w.ctx.Done(): | ||
w.cancel() | ||
} | ||
}() | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package worker | ||
|
||
import ( | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func Test_Simple_Perform(t *testing.T) { | ||
r := require.New(t) | ||
|
||
var hit bool | ||
wg := &sync.WaitGroup{} | ||
wg.Add(1) | ||
w := NewSimple() | ||
w.Register("x", func(Args) error { | ||
hit = true | ||
wg.Done() | ||
return nil | ||
}) | ||
w.Perform(Job{ | ||
Handler: "x", | ||
}) | ||
wg.Wait() | ||
r.True(hit) | ||
} | ||
|
||
func Test_Simple_PerformAt(t *testing.T) { | ||
r := require.New(t) | ||
|
||
var hit bool | ||
wg := &sync.WaitGroup{} | ||
wg.Add(1) | ||
w := NewSimple() | ||
w.Register("x", func(Args) error { | ||
hit = true | ||
wg.Done() | ||
return nil | ||
}) | ||
w.PerformAt(Job{ | ||
Handler: "x", | ||
}, time.Now().Add(5*time.Millisecond)) | ||
wg.Wait() | ||
r.True(hit) | ||
} | ||
|
||
func Test_Simple_PerformIn(t *testing.T) { | ||
r := require.New(t) | ||
|
||
var hit bool | ||
wg := &sync.WaitGroup{} | ||
wg.Add(1) | ||
w := NewSimple() | ||
w.Register("x", func(Args) error { | ||
hit = true | ||
wg.Done() | ||
return nil | ||
}) | ||
w.PerformIn(Job{ | ||
Handler: "x", | ||
}, 5*time.Millisecond) | ||
wg.Wait() | ||
r.True(hit) | ||
} | ||
|
||
func Test_Simple_NoHandler(t *testing.T) { | ||
r := require.New(t) | ||
|
||
w := NewSimple() | ||
err := w.Perform(Job{}) | ||
r.Error(err) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package worker | ||
|
||
import ( | ||
"context" | ||
"time" | ||
) | ||
|
||
// Handler function that will be run by the worker and given | ||
// a slice of arguments | ||
type Handler func(Args) error | ||
|
||
// Worker interface that needs to be implemented to be considered | ||
// a "worker" | ||
type Worker interface { | ||
// Start the worker with the given context | ||
Start(context.Context) error | ||
// Stop the worker | ||
Stop() error | ||
// Perform a job as soon as possibly | ||
Perform(Job) error | ||
// PerformAt performs a job at a particular time | ||
PerformAt(Job, time.Time) error | ||
// PerformIn performs a job after waiting for a specified amount of time | ||
PerformIn(Job, time.Duration) error | ||
// Register a Handler | ||
Register(string, Handler) error | ||
} |