mikespook/gearman-go

Custom work logic

punkeel opened this issue · 8 comments

Hello,

I do need to use sleep right after I send data back. I could as well just sleep in the worker's thread, but that would be ugly : response would be delayed, and that's not (at all!) what I want.

Using php-gearman, I am able to do this :

while (1) {
  $ret = $worker->work();
  sleep(1);
}

Would that be possible with gearman-go, too ? :)

Well, if I met this circumstance, I would use a wrap function. Such as:

func Sleep(f worker.JobFunc, d time.Duration) worker.JobFunc {
    return func(job worker.Job) (r []byte, err error) {
        r,err = f(job)
        time.Sleep(d)
        return
    }
}

And

w.AddFunc("ToUpper", Sleep(ToUpper, 1), worker.Unlimited)

I'm not fan of this because my worker returns a value, and the sleep is going to delay it (slowing down the whole process).

Yes, it will be. So, if I need to custom the worker function, I'd like to use such wrap functions. But I don't understand why did you add the sleep into the PHP worker.

I have an API that allows me to query it once per second, and multiple workers that are able to call that same API.
When a worker makes a request, it returns the response and must sleep(1) to take the rate limit into account. Thing is, sleeping before returning is slowing down the whole process : the worker "has" the response, but does not return it; even though there might be no work to be done after.

With "my" version, sleeping once the result is returned, the worker sends its response, then refuses to work for one second.

I think the code has the same logic as you need.

func SleepAfterDone(f worker.JobFunc, d time.Duration) worker.JobFunc {
    return func(job worker.Job) (r []byte, err error) {
        r,err = f(job) // first, do the job,
        time.Sleep(d) // then sleep with d seconds.
        return
    }
}

And

func SleepBeforeDo(f worker.JobFunc, d time.Duration) worker.JobFunc {
    return func(job worker.Job) (r []byte, err error) {
        time.Sleep(d) // sleep d seconds firstly, 
        r,err = f(job) // then do the job
        return
    }
}

Returning a result is part of the job (does your method even return something?) ;-)

Yes, you are right. You need the return value the earlier the better.

How about:

func BlockBeforeDo(f worker.JobFunc) worker.JobFunc {
    return func(job worker.Job) (r []byte, err error) {
        getSemaphore() // get a global semaphore here, using channel magic and time package
        defer releaseSemaphore() // release the semaphore
        return f(job) // do the job,
    }
}

Then don't put a fixed time period to stop the executing, using semaphore should be better. I haven't thought about How to implement the semaphore yet. But it should not too hard to write such thing.

👍 Thanks!