Skip to content

Commit a2891e8

Browse files
Merge pull request #392 from iExecBlockchainComputing/feature/final-deadline-handling
Feature/final deadline handling
2 parents ad6a99f + 95970ee commit a2891e8

19 files changed

+576
-545
lines changed

src/main/java/com/iexec/core/configuration/CronConfiguration.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ public class CronConfiguration {
2929
@Value("${cron.detector.chain.finalize}")
3030
private int finalize;
3131

32+
@Value("${cron.detector.chain.final-deadline}")
33+
private int finalDeadline;
34+
3235
@Value("${cron.detector.timeout.contribute}")
3336
private int contributeTimeout;
3437

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2020 IEXEC BLOCKCHAIN TECH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iexec.core.detector.task;
18+
19+
import com.iexec.core.detector.Detector;
20+
import com.iexec.core.task.Task;
21+
import com.iexec.core.task.TaskService;
22+
import lombok.extern.slf4j.Slf4j;
23+
import org.springframework.scheduling.annotation.Scheduled;
24+
import org.springframework.stereotype.Service;
25+
26+
import java.util.Date;
27+
28+
@Slf4j
29+
@Service
30+
public class FinalDeadlineTaskDetector implements Detector {
31+
32+
private final TaskService taskService;
33+
34+
public FinalDeadlineTaskDetector(TaskService taskService) {
35+
this.taskService = taskService;
36+
}
37+
38+
@Scheduled(fixedRateString = "#{@cronConfiguration.getFinalDeadline()}")
39+
@Override
40+
public void detect() {
41+
log.debug("Trying to detect final deadline");
42+
for (Task task : taskService.getTasksWhereFinalDeadlineIsPossible()) {
43+
Date now = new Date();
44+
if (now.after(task.getFinalDeadline())) {
45+
log.info("Task after final deadline found [chainTaskId:{}]", task.getChainTaskId());
46+
taskService.updateTask(task.getChainTaskId());
47+
}
48+
}
49+
}
50+
}

src/main/java/com/iexec/core/replicate/ReplicateSupplyService.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
import org.springframework.stereotype.Service;
3939

4040
import java.util.*;
41-
import java.util.concurrent.CompletableFuture;
4241
import java.util.stream.Collectors;
4342

4443
import static com.iexec.common.replicate.ReplicateStatus.*;
@@ -359,9 +358,7 @@ private Optional<TaskNotificationType> recoverReplicateInRevealPhase(Task task,
359358
if (didReplicateStartRevealing && didReplicateRevealOnChain) {
360359
ReplicateStatusDetails details = new ReplicateStatusDetails(blockNumber);
361360
replicatesService.updateReplicateStatus(chainTaskId, walletAddress, REVEALED, details);
362-
363-
CompletableFuture<Void> completableFuture = taskService.updateTask(chainTaskId);
364-
completableFuture.join();
361+
taskService.updateTask(chainTaskId).join();
365362
}
366363

367364
// we read the replicate from db to consider the changes added in the previous case

src/main/java/com/iexec/core/task/TaskService.java

Lines changed: 45 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,50 +25,48 @@
2525
import com.iexec.core.replicate.Replicate;
2626
import com.iexec.core.replicate.ReplicatesService;
2727
import com.iexec.core.task.event.*;
28-
import com.iexec.core.task.executor.TaskExecutorEngine;
28+
import com.iexec.core.task.update.TaskUpdateRequestConsumer;
29+
import com.iexec.core.task.update.TaskUpdateRequestManager;
2930
import lombok.extern.slf4j.Slf4j;
3031
import org.apache.commons.lang3.tuple.Pair;
3132
import org.springframework.context.ApplicationEventPublisher;
3233
import org.springframework.stereotype.Service;
3334

34-
import java.util.Arrays;
35-
import java.util.Date;
36-
import java.util.List;
37-
import java.util.Optional;
38-
import java.util.concurrent.CompletableFuture;
39-
import java.util.concurrent.ConcurrentHashMap;
35+
import java.util.*;
36+
import java.util.concurrent.*;
4037
import java.util.stream.Collectors;
4138

4239
import static com.iexec.core.task.TaskStatus.*;
4340

4441
@Slf4j
4542
@Service
46-
public class TaskService {
43+
public class TaskService implements TaskUpdateRequestConsumer {
4744

4845
private final ConcurrentHashMap<String, Boolean>
4946
taskAccessForNewReplicateLock = new ConcurrentHashMap<>();
5047

51-
private TaskRepository taskRepository;
52-
private TaskExecutorEngine taskExecutorEngine;
53-
private IexecHubService iexecHubService;
54-
private ReplicatesService replicatesService;
55-
private ApplicationEventPublisher applicationEventPublisher;
56-
private Web3jService web3jService;
48+
private final TaskRepository taskRepository;
49+
private final TaskUpdateRequestManager taskUpdateRequestManager;
50+
private final IexecHubService iexecHubService;
51+
private final ReplicatesService replicatesService;
52+
private final ApplicationEventPublisher applicationEventPublisher;
53+
private final Web3jService web3jService;
5754

5855
public TaskService(
5956
TaskRepository taskRepository,
60-
TaskExecutorEngine taskExecutorEngine,
57+
TaskUpdateRequestManager taskUpdateRequestManager,
6158
IexecHubService iexecHubService,
6259
ReplicatesService replicatesService,
6360
ApplicationEventPublisher applicationEventPublisher,
6461
Web3jService web3jService
6562
) {
6663
this.taskRepository = taskRepository;
67-
this.taskExecutorEngine = taskExecutorEngine;
64+
this.taskUpdateRequestManager = taskUpdateRequestManager;
6865
this.iexecHubService = iexecHubService;
6966
this.replicatesService = replicatesService;
7067
this.applicationEventPublisher = applicationEventPublisher;
7168
this.web3jService = web3jService;
69+
this.taskUpdateRequestManager.setRequestConsumer(this);
7270
}
7371

7472
/**
@@ -143,6 +141,10 @@ public List<Task> getTasksInNonFinalStatuses() {
143141
return taskRepository.findByCurrentStatusNotIn(TaskStatus.getFinalStatuses());
144142
}
145143

144+
public List<Task> getTasksWhereFinalDeadlineIsPossible() {
145+
return taskRepository.findByCurrentStatusNotIn(TaskStatus.getStatusesWhereFinalDeadlineIsImpossible());
146+
}
147+
146148
public List<String> getChainTaskIdsOfTasksExpiredBefore(Date expirationDate) {
147149
return taskRepository.findChainTaskIdsByFinalDeadlineBefore(expirationDate)
148150
.stream()
@@ -191,47 +193,22 @@ public Date getTaskFinalDeadline(String chainTaskId) {
191193
}
192194

193195
/**
194-
* Update task asynchronously.
195-
*
196+
* Async method for requesting task update
196197
* @param chainTaskId
197198
* @return
198199
*/
199-
// TODO change this mechanism of update
200-
public CompletableFuture<Void> updateTask(String chainTaskId) {
201-
Optional<Task> oTask = getTaskByChainTaskId(chainTaskId);
202-
if (oTask.isEmpty()) {
203-
return CompletableFuture.completedFuture(null);
204-
}
205-
Task task = oTask.get();
206-
Date finalDeadline = task.getFinalDeadline();
207-
if (finalDeadline == null) {
208-
log.error("Cannot update task without final deadline [chainTaskId:{}]",
209-
chainTaskId);
210-
return CompletableFuture.completedFuture(null);
211-
}
212-
long expiration = finalDeadline.getTime() - new Date().getTime();
213-
return taskExecutorEngine.run(
214-
chainTaskId,
215-
expiration,
216-
() -> updateTaskRunnable(chainTaskId)
217-
);
200+
public CompletableFuture<Boolean> updateTask(String chainTaskId) {
201+
return taskUpdateRequestManager.publishRequest(chainTaskId);
218202
}
219203

220204
/**
221-
* Remove task's executor if task is
222-
* in final status.
223-
*
224-
* @param task
205+
* Async called when a task update request is received
206+
* @param chainTaskId
225207
*/
226-
public void removeTaskExecutor(Task task) {
227-
if (!TaskStatus.isFinalStatus(task.getCurrentStatus())) {
228-
log.error("Cannot remove executor for unfinished " +
229-
"task [chainTaskId:{}]", task.getChainTaskId());
230-
return;
231-
}
232-
taskExecutorEngine.removeExecutor(task.getChainTaskId());
233-
log.info("Removed task executor [chainTaskId:{}]",
234-
task.getChainTaskId());
208+
@Override
209+
public void onTaskUpdateRequest(String chainTaskId) {
210+
log.info("Received task update request [chainTaskId:{}]", chainTaskId);
211+
this.updateTaskRunnable(chainTaskId);
235212
}
236213

237214
void updateTaskRunnable(String chainTaskId) {
@@ -240,8 +217,20 @@ void updateTaskRunnable(String chainTaskId) {
240217
return;
241218
}
242219
Task task = optional.get();
220+
TaskStatus currentStatus = task.getCurrentStatus();
221+
222+
boolean isFinalDeadlinePossible =
223+
!TaskStatus.getStatusesWhereFinalDeadlineIsImpossible().contains(currentStatus);
224+
if (isFinalDeadlinePossible && new Date().after(task.getFinalDeadline())){
225+
updateTaskStatusAndSave(task, FINAL_DEADLINE_REACHED);
226+
// Eventually should fire a "final deadline reached" notification to worker,
227+
// but here let's just trigger an updateTask() leading to a failed status
228+
// which will itself fire a generic "abort" notification
229+
updateTask(chainTaskId);
230+
return;
231+
}
243232

244-
switch (task.getCurrentStatus()) {
233+
switch (currentStatus) {
245234
case RECEIVED:
246235
received2Initialized(task);
247236
break;
@@ -303,9 +292,11 @@ void updateTaskRunnable(String chainTaskId) {
303292
case FINALIZE_FAILED:
304293
toFailed(task);
305294
break;
295+
case FINAL_DEADLINE_REACHED:
296+
toFailed(task);
297+
break;
306298
case COMPLETED:
307299
case FAILED:
308-
removeTaskExecutor(task);
309300
break;
310301
}
311302
}
@@ -692,6 +683,7 @@ private void finalizedToCompleted(Task task) {
692683

693684
private void toFailed(Task task) {
694685
updateTaskStatusAndSave(task, FAILED);
686+
applicationEventPublisher.publishEvent(new TaskFailedEvent(task.getChainTaskId()));
695687
}
696688
public void initializeTaskAccessForNewReplicateLock(String chainTaskId) {
697689
taskAccessForNewReplicateLock.putIfAbsent(chainTaskId, false);
@@ -713,4 +705,5 @@ private void setTaskAccessForNewReplicateLock(String chainTaskId, boolean isTask
713705
taskAccessForNewReplicateLock.replace(chainTaskId, isTaskBeingAccessedForNewReplicate);
714706
}
715707

708+
716709
}

src/main/java/com/iexec/core/task/TaskStatus.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
package com.iexec.core.task;
1818

19+
import java.util.ArrayList;
1920
import java.util.Arrays;
21+
import java.util.Collections;
2022
import java.util.List;
2123

2224
public enum TaskStatus {
@@ -39,6 +41,7 @@ public enum TaskStatus {
3941
FINALIZING,
4042
FINALIZED,
4143
FINALIZE_FAILED,
44+
FINAL_DEADLINE_REACHED,
4245
COMPLETED,
4346
FAILED;
4447

@@ -65,6 +68,12 @@ public static List<TaskStatus> getFinalStatuses() {
6568
return List.of(FAILED, COMPLETED);
6669
}
6770

71+
public static List<TaskStatus> getStatusesWhereFinalDeadlineIsImpossible() {
72+
List<TaskStatus> excludedStatuses = new ArrayList<>(getFinalStatuses());
73+
Collections.addAll(excludedStatuses, FINAL_DEADLINE_REACHED);
74+
return excludedStatuses;
75+
}
76+
6877
public static boolean isInContributionPhase(TaskStatus status) {
6978
return getWaitingContributionStatuses().contains(status);
7079
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright 2020 IEXEC BLOCKCHAIN TECH
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.iexec.core.task.event;
18+
19+
import lombok.*;
20+
21+
@AllArgsConstructor
22+
@Getter
23+
public class TaskFailedEvent {
24+
25+
private String chainTaskId;
26+
}

src/main/java/com/iexec/core/task/executor/TaskExecutorEngine.java

Lines changed: 0 additions & 60 deletions
This file was deleted.

0 commit comments

Comments
 (0)