NIFI-6807: When a Controller Service's state is transitioned to ENABLING, complete the Future successfully, even if the Controller Service is not valid. The Controller Service will remain in the ENABLING state until it is made valid, at which point it will ENABLE (unless explicitly disabled first). This allows us to Enable a Controller Service and its referencing components, even if the referencing component is still invalid due to it not yet recognizing the the referenced service has been enabled.

This closes #3841
This commit is contained in:
Mark Payne 2019-10-24 10:57:54 -04:00 committed by Matt Gilman
parent 81fc2768c9
commit 3f270f184c
No known key found for this signature in database
GPG Key ID: DF61EC19432AEE37
3 changed files with 7 additions and 15 deletions

View File

@ -66,7 +66,7 @@ public class ServiceStateTransition {
validateReferences(serviceNode);
enabledFutures.stream().forEach(future -> future.complete(null));
enabledFutures.forEach(future -> future.complete(null));
return true;
} finally {
writeLock.unlock();

View File

@ -413,8 +413,9 @@ public class StandardControllerServiceNode extends AbstractComponentNode impleme
final ConfigurationContext configContext = new StandardConfigurationContext(StandardControllerServiceNode.this, controllerServiceProvider, null, getVariableRegistry());
if (!isActive()) {
LOG.debug("{} is no longer active so will not attempt to enable it", StandardControllerServiceNode.this);
LOG.warn("{} is no longer active so will no longer attempt to enable it", StandardControllerServiceNode.this);
stateTransition.disable();
future.complete(null);
return;
}
@ -433,15 +434,16 @@ public class StandardControllerServiceNode extends AbstractComponentNode impleme
boolean shouldEnable;
synchronized (active) {
shouldEnable = active.get() && stateTransition.enable();
shouldEnable = active.get() && stateTransition.enable(); // Transitioning the state to ENABLED will complete our future.
}
if (!shouldEnable) {
LOG.debug("Disabling service {} after it has been enabled due to disable action being initiated.", service);
LOG.info("Disabling service {} after it has been enabled due to disable action being initiated.", service);
// Can only happen if user initiated DISABLE operation before service finished enabling. It's state will be
// set to DISABLING (see disable() operation)
invokeDisable(configContext);
stateTransition.disable();
future.complete(null);
} else {
LOG.info("Successfully enabled {}", service);
}

View File

@ -46,7 +46,6 @@ import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@ -571,16 +570,7 @@ public class StandardControllerServiceProvider implements ControllerServiceProvi
for (final ControllerServiceNode nodeToEnable : recursiveReferences) {
if (!nodeToEnable.isActive()) {
logger.debug("Enabling {} because it references {}", nodeToEnable, serviceNode);
final Future<?> enableFuture = enableControllerService(nodeToEnable);
try {
enableFuture.get();
} catch (final ExecutionException ee) {
throw new IllegalStateException("Failed to enable Controller Service " + nodeToEnable, ee.getCause());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException("Interrupted while enabling Controller Service");
}
enableControllerService(nodeToEnable);
updated.add(nodeToEnable);
}
}