package me.hao0.antares.client.job.execute;
import com.google.common.annotations.Beta;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import me.hao0.antares.client.core.AntaresClient;
import me.hao0.antares.client.job.Job;
import me.hao0.antares.common.dto.PullShard;
import me.hao0.antares.common.dto.ShardFinishDto;
import me.hao0.antares.common.dto.ShardOperateResp;
import me.hao0.antares.common.dto.ShardPullResp;
import me.hao0.antares.common.model.enums.ShardOperateRespCode;
import me.hao0.antares.common.retry.Retryer;
import me.hao0.antares.common.retry.Retryers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
/**
* The default job executor
* Author: haolin
* Email: haolin.h0@gmail.com
*/
@Beta
public class DefaultJobExecutor extends AbstractJobExecutor implements JobExecutor {
private static final Logger log = LoggerFactory.getLogger(DefaultJobExecutor.class);
private final ConcurrentHashMap<Job, JobRetryer> jobRetryers = new ConcurrentHashMap<>();
public DefaultJobExecutor(AntaresClient client) {
super(client);
}
@Override
protected PullShard pullShard(Long instanceId, ZkJob zkJob) {
JobRetryer retryer = getJobRetryer(zkJob.getJob());
ShardPullResp pullResp;
try {
pullResp = retryer.getPullRetryer().call(new RetryablePullShardTask(instanceId));
} catch (Exception e) {
log.error("failed to pull the shard, cause: {}", Throwables.getStackTraceAsString(e));
return null;
}
if (pullResp == null) {
return null;
}
checkInvalidInstance(instanceId, zkJob, pullResp.getCode());
return pullResp.getPullShard();
}
@Override
protected Boolean returnShard(Long instanceId, Long shardId, ZkJob zkJob) {
JobRetryer retryer = getJobRetryer(zkJob.getJob());
ShardOperateResp returnResp;
try {
returnResp = retryer.getReturnRetryer().call(new RetryableReturnShardTask(instanceId, shardId));
} catch (Exception e) {
log.error("failed to return the shard(jobInstanceId={}, shardId={}), cause: {}", instanceId, shardId, Throwables.getStackTraceAsString(e));
return Boolean.FALSE;
}
if (returnResp == null) {
return Boolean.TRUE;
} else {
checkInvalidInstance(instanceId, zkJob, returnResp.getCode());
}
return returnResp.getSuccess();
}
@Override
protected Boolean finishShard(ShardFinishDto shardFinishDto, ZkJob zkJob) {
JobRetryer retryer = getJobRetryer(zkJob.getJob());
ShardOperateResp finishResp;
try {
finishResp = retryer.getFinishRetryer().call(new RetryableFinishShardTask(shardFinishDto));
} catch (Exception e) {
log.error("failed to finish the shard({}), cause: {}", shardFinishDto, Throwables.getStackTraceAsString(e));
return Boolean.FALSE;
}
if (finishResp == null) {
return Boolean.TRUE;
} else {
checkInvalidInstance(shardFinishDto.getInstanceId(), zkJob, finishResp.getCode());
}
return finishResp.getSuccess();
}
/**
* Pull shard task
*/
private class RetryablePullShardTask implements Callable<ShardPullResp> {
private Long jobInstanceId;
public RetryablePullShardTask(Long jobInstanceId) {
this.jobInstanceId = jobInstanceId;
}
@Override
public ShardPullResp call() throws Exception {
return client.getHttp().pullJobInstanceShard(jobInstanceId);
}
}
/**
* Finish shard task
*/
private class RetryableFinishShardTask implements Callable<ShardOperateResp> {
private ShardFinishDto shardFinishDto;
public RetryableFinishShardTask(ShardFinishDto shardFinishDto) {
this.shardFinishDto = shardFinishDto;
}
@Override
public ShardOperateResp call() throws Exception {
return client.getHttp().finishJobInstanceShard(shardFinishDto);
}
}
/**
* Push shard task
*/
private class RetryableReturnShardTask implements Callable<ShardOperateResp> {
private Long instanceId;
private Long shardId;
public RetryableReturnShardTask(Long instanceId, Long shardId) {
this.instanceId = instanceId;
this.shardId = shardId;
}
@Override
public ShardOperateResp call() throws Exception {
return client.getHttp().returnJobInstanceShard(instanceId, shardId);
}
}
private JobRetryer getJobRetryer(Job job) {
JobRetryer retryer = jobRetryers.get(job);
if (retryer == null){
Retryer<ShardPullResp> pullShardRetryer = Retryers.get().newRetryer(new Predicate<ShardPullResp>() {
@Override
public boolean apply(ShardPullResp shardPullResp) {
return ShardOperateRespCode.needPullAgain(shardPullResp.getCode());
}
}, 5);
Retryer<ShardOperateResp> finishShardRetryer = Retryers.get().newRetryer(new Predicate<ShardOperateResp>() {
@Override
public boolean apply(ShardOperateResp finishResp) {
return ShardOperateRespCode.needFinishAgain(finishResp.getCode());
}
}, 5);
Retryer<ShardOperateResp> returnShardRetryer = Retryers.get().newRetryer(new Predicate<ShardOperateResp>() {
@Override
public boolean apply(ShardOperateResp returnResp) {
return ShardOperateRespCode.needReturnAgain(returnResp.getCode());
}
}, 5);
retryer = new JobRetryer(job, pullShardRetryer, finishShardRetryer, returnShardRetryer);
jobRetryers.putIfAbsent(job, retryer);
}
return retryer;
}
}