我正在尝试使用Play中的异步作业运行长时间运行的任务(不阻止HTTP请求执行池)。 Play中的Jobs完全由框架管理,因此Play将负责数据库连接和事务。这意味着作业在单个事务中作为普通调用运行。这对于大多数用例来说都很棒,但是如果你需要在作业完成之前提交一些数据呢?
嗯,根据Guillaume Bort,一个选项是
将您的流程分成几个小工作
他给出了以下例子:
@On("0 0 12 * * ?")
public class UpdateCounters extends Job {
public void doJob() {
List<Employee> empList = Employee.find("long and boring SQL :)").fetch();
for(Employee emp : empList) {
new UpdateEmployeeJob(emp).now().get();
}
}
}
这很酷......每个新工作都有自己的事务,所以当这个新工作完成时,它会提交数据。干净简单! 但是如果你不想这样做呢?假设您需要等到数据提交后再继续初始作业吗?
通常,您将从新作业中检索Promise并调用await(jobPromise)但这在作业中不起作用(等待仅在扩展Controller时可用且未在作业上实现)...
那么在新的较小作业完成之前暂停初始作业的最佳方法是什么?
答案 0 :(得分:8)
在查看Job是否完成(不使用数据库)的不同方法之后,我注意到Play Promise实现了Future接口,因此具有isDone()操作,可以使用这个检查任务是否完成。
更好的是,Future接口还定义了一个get()方法
等待计算完成所需,然后检索 结果。
因此暂停初始作业直到新的较小作业完成,可以通过调用Promise上的get()来实现,在较小的作业上调用now()时返回(就像Guillaume的例子中所做的那样)
谢谢!
答案 1 :(得分:1)
也许您可以使用以下代码进行批量提交:
Employee.em().getTransaction().commit();
Employee.em().getTransaction().begin();
Employee.em().flush();
Employee.em().clear();
答案 2 :(得分:0)
考虑到Play的本质,我会说唯一的方法是拥有一些标志(通常在数据库中),“父亲”作业可以在循环中检查并阻止自己等待它。
例如:
//take it as an idea, not as working code as it may have typos and dragons
public void doJob() {
String uuid = generateUUID();
List<Employee> empList = Employee.find("long and boring SQL :)").fetch();
for(Employee emp : empList) {
//update employee giving uuid so we know this job is related to this parent
new UpdateEmployeeJob(emp, uuid).now().get();
}
int done = 0;
while(done < empList.size()) {
//query db for number of instances updated with this uuid
//(assuming instance updated once child job is done)
done = Update.count("uuid = ?1",uuid);
}
}
可以管理它