use of com.netflix.titus.common.framework.reconciler.EntityHolder in project titus-control-plane by Netflix.
the class ServiceDifferenceResolver method applyStore.
private List<ChangeAction> applyStore(ReconciliationEngine<JobManagerReconcilerEvent> engine, ServiceJobView refJobView, EntityHolder storeJob, AtomicInteger allowedNewTasks) {
if (!storeWriteRetryInterceptor.executionLimits(storeJob)) {
return Collections.emptyList();
}
List<ChangeAction> actions = new ArrayList<>();
EntityHolder refJobHolder = refJobView.getJobHolder();
Job<ServiceJobExt> refJob = refJobHolder.getEntity();
if (!refJobHolder.getEntity().equals(storeJob.getEntity())) {
actions.add(storeWriteRetryInterceptor.apply(BasicJobActions.updateJobInStore(engine, jobStore)));
}
boolean isJobTerminating = refJob.getStatus().getState() == JobState.KillInitiated;
for (EntityHolder referenceTaskHolder : refJobHolder.getChildren()) {
ServiceJobTask refTask = referenceTaskHolder.getEntity();
Optional<EntityHolder> storeHolder = storeJob.findById(referenceTaskHolder.getId());
ServiceJobTask storeTask = storeHolder.get().getEntity();
boolean refAndStoreInSync = areEquivalent(storeHolder.get(), referenceTaskHolder);
boolean shouldRetry = !isJobTerminating && refTask.getStatus().getState() == TaskState.Finished && !refTask.getStatus().getReasonCode().equals(TaskStatus.REASON_SCALED_DOWN) && allowedNewTasks.get() > 0;
if (refAndStoreInSync) {
TaskState currentTaskState = refTask.getStatus().getState();
if (currentTaskState == TaskState.Finished) {
if (isJobTerminating || isScaledDown(storeTask) || hasEnoughTasksRunning(refJobView)) {
actions.add(removeFinishedServiceTaskAction(jobStore, storeTask));
} else if (shouldRetry && TaskRetryers.shouldRetryNow(referenceTaskHolder, clock)) {
createNewTaskAction(refJobView, Optional.of(referenceTaskHolder), Collections.emptyList(), Collections.emptyList()).ifPresent(actions::add);
}
}
} else {
Task task = referenceTaskHolder.getEntity();
CallMetadata callMetadata = RECONCILER_CALLMETADATA.toBuilder().withCallReason("Writing runtime state changes to store").build();
actions.add(storeWriteRetryInterceptor.apply(BasicTaskActions.writeReferenceTaskToStore(jobStore, engine, task.getId(), callMetadata, titusRuntime)));
}
// Both current and delayed retries are counted
if (shouldRetry) {
allowedNewTasks.decrementAndGet();
}
}
return actions;
}
use of com.netflix.titus.common.framework.reconciler.EntityHolder in project titus-control-plane by Netflix.
the class DefaultReconciliationFrameworkTest method testMultiEngineChangeActionWithInvalidEngineId.
@Test
public void testMultiEngineChangeActionWithInvalidEngineId() {
EntityHolder root1 = EntityHolder.newRoot("myRoot1", "myEntity1");
framework.newEngine(root1).subscribe();
testScheduler.triggerActions();
Observable<Void> multiChangeObservable = framework.changeReferenceModel(// Keep anonymous class instead of lambda for readability
new MultiEngineChangeAction() {
@Override
public Observable<Map<String, List<ModelActionHolder>>> apply() {
return Observable.error(new IllegalStateException("invocation not expected"));
}
}, // Keep anonymous class instead of lambda for readability
(id, modelUpdates) -> new ChangeAction() {
@Override
public Observable<List<ModelActionHolder>> apply() {
return Observable.error(new IllegalStateException("invocation not expected"));
}
}, "myRoot1", "badRootId");
ExtTestSubscriber<Void> multiChangeSubscriber = new ExtTestSubscriber<>();
multiChangeObservable.subscribe(multiChangeSubscriber);
assertThat(multiChangeSubscriber.isError()).isTrue();
assertThat(multiChangeSubscriber.getError().getMessage()).contains("badRootId");
}
use of com.netflix.titus.common.framework.reconciler.EntityHolder in project titus-control-plane by Netflix.
the class TaskTimeoutChangeActionsTest method testTimeout.
@Test
public void testTimeout() throws Exception {
BatchJobTask launchedTask = createTaskInState(TaskState.Launched);
EntityHolder initialRoot = rootFrom(job, launchedTask);
EntityHolder initialChild = first(initialRoot.getChildren());
// Initially there is no timeout associated
TimeoutStatus timeoutStatus = TaskTimeoutChangeActions.getTimeoutStatus(initialChild, testClock);
assertThat(timeoutStatus).isEqualTo(TimeoutStatus.NotSet);
// Apply timeout
List<ModelActionHolder> modelActionHolders = TaskTimeoutChangeActions.setTimeout(launchedTask.getId(), launchedTask.getStatus().getState(), DEADLINE_INTERVAL_MS, testClock).apply().toBlocking().first();
EntityHolder rootWithTimeout = modelActionHolders.get(0).getAction().apply(initialRoot).get().getLeft();
assertThat(TaskTimeoutChangeActions.getTimeoutStatus(first(rootWithTimeout.getChildren()), testClock)).isEqualTo(TimeoutStatus.Pending);
// Advance time to trigger timeout
testClock.advanceTime(DEADLINE_INTERVAL_MS, TimeUnit.MILLISECONDS);
assertThat(TaskTimeoutChangeActions.getTimeoutStatus(first(rootWithTimeout.getChildren()), testClock)).isEqualTo(TimeoutStatus.TimedOut);
}
use of com.netflix.titus.common.framework.reconciler.EntityHolder in project titus-control-plane by Netflix.
the class RateLimiterInterceptorTest method testRateLimiting.
@Test
public void testRateLimiting() throws Exception {
Job<BatchJobExt> job = batchJobs(JobDescriptorGenerator.oneTaskBatchJobDescriptor()).getValue();
// Use all tokens
EntityHolder nextRoot = EntityHolder.newRoot("root", job);
for (int i = 0; i < BUCKET_SIZE; i++) {
assertThat(rateLimiterInterceptor.executionLimits(nextRoot)).isEqualTo(BUCKET_SIZE - i);
ModelAction updateAction = executeRateLimitedAction(SampleTitusChangeActions.successfulJob());
nextRoot = updateAction.apply(nextRoot).get().getRight();
}
assertThat(rateLimiterInterceptor.executionLimits(nextRoot)).isEqualTo(0);
// Refill
testClock.advanceTime(REFILL_INTERVAL_MS, TimeUnit.MILLISECONDS);
assertThat(rateLimiterInterceptor.executionLimits(nextRoot)).isEqualTo(1);
}
use of com.netflix.titus.common.framework.reconciler.EntityHolder in project titus-control-plane by Netflix.
the class SingleTransaction method applyModelUpdate.
private Optional<EntityHolder> applyModelUpdate(ModelActionHolder updateAction, EntityHolder rootHolder) {
ReconcileEventFactory<EVENT> eventFactory = engine.getEventFactory();
try {
return updateAction.getAction().apply(rootHolder).map(newRootAndChangedItem -> {
EntityHolder newRoot = newRootAndChangedItem.getLeft();
EntityHolder changedItem = newRootAndChangedItem.getRight();
Optional<EntityHolder> previousHolder = rootHolder.findById(changedItem.getId());
modelEventQueue.add(eventFactory.newModelUpdateEvent(engine, changeAction, updateAction, changedItem, previousHolder, transactionId));
return newRoot;
});
} catch (Exception e) {
modelEventQueue.add(eventFactory.newModelUpdateErrorEvent(engine, changeAction, updateAction, rootHolder, e, transactionId));
logger.warn("Failed to update state of {} ({})", rootHolder.getId(), e.toString());
throw e;
}
}
Aggregations