Best pattern for giving completion Promises to observers

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Best pattern for giving completion Promises to observers

Ron Hitchens
I'd like some guidance on the best practice for creating Promises that I can use to notify observers at some point in the future from a forked execution.

I have a job batching system (ok, this is about the third re-write of it) that enqueues jobs to be run asynchronously on forked Executions.  An HTTP request drops the information about the job in a DB-resident queue and then at some point later the job will be picked up and run.  This part is all fine, and I have a function (Promise<Job> Jobs.startNextJob()) which launches and returns a Promise<Job>.  Straightforward.

What I want to do is provide a method on Job (Promise<JobResult> whenComplete()) that an observer can call to receive a JobResult when the Job execution has completed.  There are two immediate uses for this (pseudo code):

Use Case 1, creating new job with the option of waiting for it or spinning it off asynchronously:

void handler() {
    Jobs.startNextJob().then { Job job ->
        if (job == null) { set 404 http result, return }
        if (async option) {
            set header with job.id
            set 202 http result, return
        }
        job.whenComplete().then { JobResult jobResult ->
                set http result according to jobResult
        }
    }
}

Use Case 2, rendezvousing with a running job:

void handler() {
    Jobs.findJob(job id parameter).then { Job job ->
        if (job == null) { set 404 http result, return }
        job.whenComplete().then { JobResult jobResult ->
                set http result according to jobResult
        }
    }
}

What I want to know is the best way to hand out Promises from whenComplete() and the way to fulfill those promises when the job completes.  What I have currently, and it works, is the following.  But it feels wrong because it's tying up a thread and not making good use of the RatPack APIs.

synchronized Promise<JobResult> whenComplete() {
        if (result != null) {
                return Promise.value (result)
        }

        Promise.of { Downstream fulfiller ->
                Execution.fork().start {
                        synchronized (completionSignal) {  // some boilerplate removed
                                completionSignal.wait()
                        }
                        if (result instanceof JobErrorResult) {
                                fulfiller.error (result.result as Throwable)
                        } else {
                                fulfiller.success (result)
                        }
                }
        }
}

In the Job completion function, which sets the 'result' variable, it does a notifyAll() on the completionSignal object.

It seems like this is archaic and that there must be a more elegant way to do this.  Intuitively, I'd expect to create Promise instances and add them to a list.  Then, when the Job is complete, run the list and "fulfill" each of them.

What's the preferred way to do something like this?  To make a Promise, or Promises, to a caller that I can fulfill later, from some other context.

Thanks in advance.




Reply | Threaded
Open this post in threaded view
|

Re: Best pattern for giving completion Promises to observers

Luke Daley
Administrator
Reply | Threaded
Open this post in threaded view
|

Re: Best pattern for giving completion Promises to observers

Ron Hitchens
Thanks Luke
Reply | Threaded
Open this post in threaded view
|

Re: Best pattern for giving completion Promises to observers

johnrengelman
The Topic class has been renamed to Promised (notice the 'd' at the end) for Ratpack 1.2 - https://github.com/ratpack/ratpack/commit/a83b29152dd89fa5a2116b85cfed9949a3be52fa