2012-03-26 13:32:59 +08:00
|
|
|
package worker
|
|
|
|
|
2013-12-23 17:01:01 +08:00
|
|
|
import (
|
2014-03-09 08:22:14 +08:00
|
|
|
"bytes"
|
2014-06-11 02:37:10 +08:00
|
|
|
"errors"
|
2013-12-23 17:05:42 +08:00
|
|
|
"sync"
|
2014-06-11 02:37:10 +08:00
|
|
|
"sync/atomic"
|
2013-12-23 17:05:42 +08:00
|
|
|
"testing"
|
2014-06-04 20:31:25 +08:00
|
|
|
"time"
|
2013-12-23 17:01:01 +08:00
|
|
|
)
|
2012-03-26 13:32:59 +08:00
|
|
|
|
|
|
|
var worker *Worker
|
|
|
|
|
|
|
|
func init() {
|
2013-08-30 12:36:57 +08:00
|
|
|
worker = New(Unlimited)
|
2012-03-26 13:32:59 +08:00
|
|
|
}
|
|
|
|
|
2013-12-26 12:06:47 +08:00
|
|
|
func TestWorkerErrNoneAgents(t *testing.T) {
|
|
|
|
err := worker.Ready()
|
|
|
|
if err != ErrNoneAgents {
|
|
|
|
t.Error("ErrNoneAgents expected.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-26 13:32:59 +08:00
|
|
|
func TestWorkerAddServer(t *testing.T) {
|
2013-08-30 12:36:57 +08:00
|
|
|
t.Log("Add local server 127.0.0.1:4730.")
|
2013-12-26 15:28:42 +08:00
|
|
|
if err := worker.AddServer(Network, "127.0.0.1:4730"); err != nil {
|
2013-08-30 12:36:57 +08:00
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if l := len(worker.agents); l != 1 {
|
|
|
|
t.Log(worker.agents)
|
|
|
|
t.Error("The length of server list should be 1.")
|
|
|
|
}
|
2012-03-26 13:32:59 +08:00
|
|
|
}
|
|
|
|
|
2013-12-26 12:06:47 +08:00
|
|
|
func TestWorkerErrNoneFuncs(t *testing.T) {
|
|
|
|
err := worker.Ready()
|
|
|
|
if err != ErrNoneFuncs {
|
|
|
|
t.Error("ErrNoneFuncs expected.")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-30 18:01:10 +08:00
|
|
|
func foobar(job Job) ([]byte, error) {
|
2013-08-30 12:36:57 +08:00
|
|
|
return nil, nil
|
2012-03-26 13:32:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestWorkerAddFunction(t *testing.T) {
|
2013-08-30 12:36:57 +08:00
|
|
|
if err := worker.AddFunc("foobar", foobar, 0); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if err := worker.AddFunc("timeout", foobar, 5); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if l := len(worker.funcs); l != 2 {
|
|
|
|
t.Log(worker.funcs)
|
|
|
|
t.Errorf("The length of function map should be %d.", 2)
|
|
|
|
}
|
2012-03-26 13:32:59 +08:00
|
|
|
}
|
|
|
|
|
2012-05-24 19:21:30 +08:00
|
|
|
func TestWorkerRemoveFunc(t *testing.T) {
|
2013-08-30 12:36:57 +08:00
|
|
|
if err := worker.RemoveFunc("foobar"); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
2012-03-26 13:32:59 +08:00
|
|
|
}
|
2013-08-30 18:01:10 +08:00
|
|
|
|
|
|
|
func TestWork(t *testing.T) {
|
2013-12-23 17:01:01 +08:00
|
|
|
var wg sync.WaitGroup
|
|
|
|
worker.JobHandler = func(job Job) error {
|
|
|
|
t.Logf("%s", job.Data())
|
|
|
|
wg.Done()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := worker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
2013-08-30 18:01:10 +08:00
|
|
|
go worker.Work()
|
2013-12-23 17:01:01 +08:00
|
|
|
wg.Add(1)
|
|
|
|
worker.Echo([]byte("Hello"))
|
|
|
|
wg.Wait()
|
2013-08-30 18:01:10 +08:00
|
|
|
}
|
|
|
|
|
2014-03-09 08:22:14 +08:00
|
|
|
func TestLargeDataWork(t *testing.T) {
|
|
|
|
worker := New(Unlimited)
|
|
|
|
defer worker.Close()
|
|
|
|
|
|
|
|
if err := worker.AddServer(Network, "127.0.0.1:4730"); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
worker.Ready()
|
|
|
|
|
|
|
|
l := 5714
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
bigdataHandler := func(job Job) error {
|
|
|
|
defer wg.Done()
|
|
|
|
if len(job.Data()) != l {
|
|
|
|
t.Errorf("expected length %d. got %d.", l, len(job.Data()))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if err := worker.AddFunc("bigdata", foobar, 0); err != nil {
|
|
|
|
defer wg.Done()
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
worker.JobHandler = bigdataHandler
|
|
|
|
|
|
|
|
worker.ErrorHandler = func(err error) {
|
|
|
|
t.Fatal("shouldn't have received an error")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := worker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go worker.Work()
|
|
|
|
wg.Add(1)
|
|
|
|
|
|
|
|
// var cli *client.Client
|
|
|
|
// var err error
|
|
|
|
// if cli, err = client.New(client.Network, "127.0.0.1:4730"); err != nil {
|
|
|
|
// t.Fatal(err)
|
|
|
|
// }
|
|
|
|
// cli.ErrorHandler = func(e error) {
|
|
|
|
// t.Error(e)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// _, err = cli.Do("bigdata", bytes.Repeat([]byte("a"), l), client.JobLow, func(res *client.Response) {
|
|
|
|
// })
|
|
|
|
// if err != nil {
|
|
|
|
// t.Error(err)
|
|
|
|
// }
|
|
|
|
|
|
|
|
worker.Echo(bytes.Repeat([]byte("a"), l))
|
|
|
|
wg.Wait()
|
|
|
|
}
|
2014-06-01 23:59:57 +08:00
|
|
|
|
2013-08-30 18:01:10 +08:00
|
|
|
func TestWorkerClose(t *testing.T) {
|
|
|
|
worker.Close()
|
|
|
|
}
|
2014-06-01 23:59:57 +08:00
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
func TestWorkWithoutReady(t *testing.T) {
|
2014-06-01 23:59:57 +08:00
|
|
|
other_worker := New(Unlimited)
|
|
|
|
|
|
|
|
if err := other_worker.AddServer(Network, "127.0.0.1:4730"); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
2014-06-04 20:31:25 +08:00
|
|
|
if err := other_worker.AddFunc("gearman-go-workertest", foobar, 0); err != nil {
|
2014-06-01 23:59:57 +08:00
|
|
|
t.Error(err)
|
|
|
|
}
|
2014-06-11 02:37:10 +08:00
|
|
|
|
2014-06-04 20:31:25 +08:00
|
|
|
timeout := make(chan bool, 1)
|
2014-06-11 02:37:10 +08:00
|
|
|
done := make(chan bool, 1)
|
2014-06-01 23:59:57 +08:00
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
other_worker.JobHandler = func(j Job) error {
|
|
|
|
if !other_worker.ready {
|
|
|
|
t.Error("Worker not ready as expected")
|
2014-06-01 23:59:57 +08:00
|
|
|
}
|
2014-06-11 02:37:10 +08:00
|
|
|
done <- true
|
2014-06-01 23:59:57 +08:00
|
|
|
return nil
|
|
|
|
}
|
2014-06-04 20:31:25 +08:00
|
|
|
go func() {
|
|
|
|
time.Sleep(5 * time.Second)
|
|
|
|
timeout <- true
|
|
|
|
}()
|
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
go func() {
|
|
|
|
other_worker.Work()
|
2014-06-04 20:31:25 +08:00
|
|
|
}()
|
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
// With the all-in-one Work() we don't know if the
|
2014-06-04 20:31:25 +08:00
|
|
|
// worker is ready at this stage so we may have to wait a sec:
|
2014-06-11 02:37:10 +08:00
|
|
|
go func() {
|
2014-06-07 12:35:05 +08:00
|
|
|
tries := 5
|
2014-06-11 02:37:10 +08:00
|
|
|
for tries > 0 {
|
2014-06-04 20:31:25 +08:00
|
|
|
if other_worker.ready {
|
|
|
|
other_worker.Echo([]byte("Hello"))
|
|
|
|
break
|
|
|
|
}
|
|
|
|
|
|
|
|
// still waiting for it to be ready..
|
2014-06-07 12:35:05 +08:00
|
|
|
time.Sleep(250 * time.Millisecond)
|
2014-06-04 20:31:25 +08:00
|
|
|
tries--
|
|
|
|
}
|
|
|
|
}()
|
2014-06-11 02:37:10 +08:00
|
|
|
|
2014-06-04 20:31:25 +08:00
|
|
|
// determine if we've finished or timed out:
|
2014-06-11 02:37:10 +08:00
|
|
|
select {
|
|
|
|
case <-timeout:
|
2014-06-04 20:31:25 +08:00
|
|
|
t.Error("Test timed out waiting for the worker")
|
2014-06-11 02:37:10 +08:00
|
|
|
case <-done:
|
2014-06-04 20:31:25 +08:00
|
|
|
}
|
|
|
|
}
|
2014-06-01 23:59:57 +08:00
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
func TestWorkWithoutReadyWithPanic(t *testing.T) {
|
2014-06-04 20:31:25 +08:00
|
|
|
other_worker := New(Unlimited)
|
2014-06-11 02:37:10 +08:00
|
|
|
|
2014-06-04 20:31:25 +08:00
|
|
|
timeout := make(chan bool, 1)
|
2014-06-11 02:37:10 +08:00
|
|
|
done := make(chan bool, 1)
|
2014-06-04 20:31:25 +08:00
|
|
|
|
|
|
|
// Going to work with no worker setup.
|
|
|
|
// when Work (hopefully) calls Ready it will get an error which should cause it to panic()
|
2014-06-11 02:37:10 +08:00
|
|
|
go func() {
|
2014-06-04 20:31:25 +08:00
|
|
|
defer func() {
|
|
|
|
if err := recover(); err != nil {
|
|
|
|
done <- true
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Error("Work should raise a panic.")
|
|
|
|
done <- true
|
|
|
|
}()
|
2014-06-11 02:37:10 +08:00
|
|
|
other_worker.Work()
|
2014-06-04 20:31:25 +08:00
|
|
|
}()
|
|
|
|
go func() {
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
timeout <- true
|
|
|
|
}()
|
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
select {
|
|
|
|
case <-timeout:
|
2014-06-04 20:31:25 +08:00
|
|
|
t.Error("Test timed out waiting for the worker")
|
2014-06-11 02:37:10 +08:00
|
|
|
case <-done:
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// initWorker creates a worker and adds the localhost server to it
|
|
|
|
func initWorker(t *testing.T) *Worker {
|
|
|
|
otherWorker := New(Unlimited)
|
|
|
|
if err := otherWorker.AddServer(Network, "127.0.0.1:4730"); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
return otherWorker
|
|
|
|
}
|
|
|
|
|
|
|
|
// submitEmptyInPack sends an empty inpack with the specified fn name to the worker. It uses
|
|
|
|
// the first agent of the worker.
|
|
|
|
func submitEmptyInPack(t *testing.T, worker *Worker, function string) {
|
|
|
|
if l := len(worker.agents); l != 1 {
|
|
|
|
t.Error("The worker has no agents")
|
|
|
|
}
|
|
|
|
inpack := getInPack()
|
|
|
|
inpack.dataType = dtJobAssign
|
|
|
|
inpack.fn = function
|
|
|
|
inpack.a = worker.agents[0]
|
|
|
|
worker.in <- inpack
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestShutdownSuccessJob tests that shutdown handles active jobs that will succeed
|
|
|
|
func TestShutdownSuccessJob(t *testing.T) {
|
|
|
|
otherWorker := initWorker(t)
|
|
|
|
finishedJob := false
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
successJob := func(job Job) ([]byte, error) {
|
|
|
|
wg.Done()
|
|
|
|
// Sleep for 10ms to ensure that the shutdown waits for this to finish
|
|
|
|
time.Sleep(time.Duration(10 * time.Millisecond))
|
|
|
|
finishedJob = true
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if err := otherWorker.AddFunc("test", successJob, 0); err != nil {
|
|
|
|
t.Error(err)
|
2014-06-04 20:31:25 +08:00
|
|
|
}
|
2014-06-11 02:37:10 +08:00
|
|
|
if err := otherWorker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
submitEmptyInPack(t, otherWorker, "test")
|
|
|
|
go otherWorker.Work()
|
|
|
|
// Wait for the success_job to start so that we know we didn't shutdown before even
|
|
|
|
// beginning to process the job.
|
|
|
|
wg.Add(1)
|
|
|
|
wg.Wait()
|
|
|
|
otherWorker.Shutdown()
|
|
|
|
if !finishedJob {
|
|
|
|
t.Error("Didn't finish job")
|
|
|
|
}
|
|
|
|
}
|
2014-06-01 23:59:57 +08:00
|
|
|
|
2014-06-11 02:37:10 +08:00
|
|
|
// TestShutdownFailureJob tests that shutdown handles active jobs that will fail
|
|
|
|
func TestShutdownFailureJob(t *testing.T) {
|
|
|
|
otherWorker := initWorker(t)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
finishedJob := false
|
|
|
|
failureJob := func(job Job) ([]byte, error) {
|
|
|
|
wg.Done()
|
|
|
|
// Sleep for 10ms to ensure that shutdown waits for this to finish
|
|
|
|
time.Sleep(time.Duration(10 * time.Millisecond))
|
|
|
|
finishedJob = true
|
|
|
|
return nil, errors.New("Error!")
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := otherWorker.AddFunc("test", failureJob, 0); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if err := otherWorker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
submitEmptyInPack(t, otherWorker, "test")
|
|
|
|
go otherWorker.Work()
|
|
|
|
// Wait for the failure_job to start so that we know we didn't shutdown before even
|
|
|
|
// beginning to process the job.
|
|
|
|
wg.Add(1)
|
|
|
|
wg.Wait()
|
|
|
|
otherWorker.Shutdown()
|
|
|
|
if !finishedJob {
|
|
|
|
t.Error("Didn't finish the failed job")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSubmitMultipleJobs(t *testing.T) {
|
|
|
|
otherWorker := initWorker(t)
|
|
|
|
var startJobs sync.WaitGroup
|
|
|
|
startJobs.Add(2)
|
|
|
|
var jobsFinished int32 = 0
|
|
|
|
job := func(job Job) ([]byte, error) {
|
|
|
|
startJobs.Done()
|
|
|
|
// Sleep for 10ms to ensure that the shutdown waits for this to finish
|
|
|
|
time.Sleep(time.Duration(10 * time.Millisecond))
|
|
|
|
atomic.AddInt32(&jobsFinished, 1)
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if err := otherWorker.AddFunc("test", job, 0); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if err := otherWorker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
submitEmptyInPack(t, otherWorker, "test")
|
|
|
|
submitEmptyInPack(t, otherWorker, "test")
|
|
|
|
go otherWorker.Work()
|
|
|
|
startJobs.Wait()
|
|
|
|
otherWorker.Shutdown()
|
|
|
|
if jobsFinished != 2 {
|
|
|
|
t.Error("Didn't run both jobs")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSubmitJobAfterShutdown(t *testing.T) {
|
|
|
|
otherWorker := initWorker(t)
|
|
|
|
noRunJob := func(job Job) ([]byte, error) {
|
|
|
|
t.Error("This job shouldn't have been run")
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
if err := otherWorker.AddFunc("test", noRunJob, 0); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
}
|
|
|
|
if err := otherWorker.Ready(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
go otherWorker.Work()
|
|
|
|
otherWorker.Shutdown()
|
|
|
|
submitEmptyInPack(t, otherWorker, "test")
|
|
|
|
// Sleep for 10ms to make sure that the job doesn't run
|
|
|
|
time.Sleep(time.Duration(10 * time.Millisecond))
|
2014-06-01 23:59:57 +08:00
|
|
|
}
|