1- From bc590ca0aceb644e0bd03d48ff48e77897bccfad Mon Sep 17 00:00:00 2001
1+ From 8f7007ac0ac2f83e27666d865727335156b5053b Mon Sep 17 00:00:00 2001
22From: Andrew Kenworthy <andrew.kenworthy@stackable.tech>
33Date: Wed, 29 Apr 2026 17:32:02 +0200
44Subject: check services are disabled before calling updateControllerService
55
66---
7- ...tandardVersionedComponentSynchronizer.java | 40 ++++++++++++++++ ---
8- 1 file changed, 34 insertions(+), 6 deletions(-)
7+ ...tandardVersionedComponentSynchronizer.java | 54 ++++++++++++++-- ---
8+ 1 file changed, 40 insertions(+), 14 deletions(-)
99
1010diff --git a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
11- index 092d2f7e7b..dbdbe166f3 100644
11+ index 092d2f7e7b..7cbcc8c6dc 100644
1212--- a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
1313+++ b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/flow/synchronization/StandardVersionedComponentSynchronizer.java
14- @@ -745,17 +745,45 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
14+ @@ -270,8 +270,8 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
15+ final ProcessGroup newProcessGroup = addProcessGroup(group, processGroup, options.getComponentIdGenerator(),
16+ additions.getParameterContexts(), additions.getParameterProviders(), group);
17+ additionsBuilder.addProcessGroup(newProcessGroup);
18+ - } catch (final ProcessorInstantiationException pie) {
19+ - throw new RuntimeException(pie);
20+ + } catch (final ProcessorInstantiationException | FlowSynchronizationException e) {
21+ + throw new RuntimeException(e);
22+ }
23+ });
24+
25+ @@ -386,8 +386,8 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
26+ final ProcessGroup topLevelGroup = syncOptions.getTopLevelGroupId() == null ? group : context.getFlowManager().getGroup(syncOptions.getTopLevelGroupId());
27+ synchronize(group, versionedExternalFlow.getFlowContents(), versionedExternalFlow.getParameterContexts(),
28+ parameterProviderReferences, topLevelGroup, syncOptions.isUpdateSettings());
29+ - } catch (final ProcessorInstantiationException pie) {
30+ - throw new RuntimeException(pie);
31+ + } catch (final ProcessorInstantiationException | FlowSynchronizationException e) {
32+ + throw new RuntimeException(e);
33+ }
34+ });
35+
36+ @@ -416,7 +416,7 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
37+
38+ private void synchronize(final ProcessGroup group, final VersionedProcessGroup proposed, final Map<String, VersionedParameterContext> versionedParameterContexts,
39+ final Map<String, ParameterProviderReference> parameterProviderReferences, final ProcessGroup topLevelGroup, final boolean updateGroupSettings)
40+ - throws ProcessorInstantiationException {
41+ + throws ProcessorInstantiationException, FlowSynchronizationException {
42+
43+ // Some components, such as Processors, may have a Scheduled State of RUNNING in the proposed flow. However, if we
44+ // transition the service into the RUNNING state, and then we need to update a Connection that is connected to it,
45+ @@ -691,7 +691,7 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
46+
47+ private void synchronizeChildGroups(final ProcessGroup group, final VersionedProcessGroup proposed, final Map<String, VersionedParameterContext> versionedParameterContexts,
48+ final Map<String, ProcessGroup> childGroupsByVersionedId, final Map<String, ParameterProviderReference> parameterProviderReferences,
49+ - final ProcessGroup topLevelGroup) throws ProcessorInstantiationException {
50+ + final ProcessGroup topLevelGroup) throws ProcessorInstantiationException, FlowSynchronizationException {
51+
52+ for (final VersionedProcessGroup proposedChildGroup : proposed.getProcessGroups()) {
53+ final ProcessGroup childGroup = childGroupsByVersionedId.get(proposedChildGroup.getIdentifier());
54+ @@ -711,7 +711,7 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
55+ }
56+
57+ private void synchronizeControllerServices(final ProcessGroup group, final VersionedProcessGroup proposed, final Map<String, ControllerServiceNode> servicesByVersionedId,
58+ - final ProcessGroup topLevelGroup) {
59+ + final ProcessGroup topLevelGroup) throws FlowSynchronizationException {
60+ // Controller Services have to be handled a bit differently than other components. This is because Processors and Controller
61+ // Services may reference other Controller Services. Since we may be adding Service A, which depends on Service B, before adding
62+ // Service B, we need to ensure that we create all Controller Services first and then call updateControllerService for each
63+ @@ -745,17 +745,43 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
1564 updateControllerService(addedService, proposedService, topLevelGroup);
1665 }
1766
@@ -40,13 +89,11 @@ index 092d2f7e7b..dbdbe166f3 100644
4089+ stopControllerService(service, proposedService, stopTimeout,
4190+ syncOptions.getComponentStopTimeoutAction(),
4291+ referencesToRestart, servicesToRestart, syncOptions);
43- + } catch (final FlowSynchronizationException | TimeoutException e) {
44- + LOG.error("Failed to stop Controller Service {} before updating", service, e);
45- + throw new RuntimeException(e);
92+ + } catch (final TimeoutException e) {
93+ + throw new FlowSynchronizationException("Failed to stop Controller Service " + service + " in preparation for update", e);
4694+ } catch (final InterruptedException e) {
4795+ Thread.currentThread().interrupt();
48- + LOG.error("Interrupted while stopping Controller Service {}", service, e);
49- + throw new RuntimeException(e);
96+ + throw new FlowSynchronizationException("Interrupted while stopping Controller Service " + service, e);
5097+ }
5198+ updateControllerService(service, proposedService, topLevelGroup);
5299+ createdAndModifiedExtensions.add(new CreatedOrModifiedExtension(service, getPropertyValues(service)));
@@ -63,3 +110,12 @@ index 092d2f7e7b..dbdbe166f3 100644
63110 }
64111 }
65112
113+ @@ -1379,7 +1405,7 @@ public class StandardVersionedComponentSynchronizer implements VersionedComponen
114+
115+ private ProcessGroup addProcessGroup(final ProcessGroup destination, final VersionedProcessGroup proposed, final ComponentIdGenerator componentIdGenerator,
116+ final Map<String, VersionedParameterContext> versionedParameterContexts,
117+ - final Map<String, ParameterProviderReference> parameterProviderReferences, ProcessGroup topLevelGroup) throws ProcessorInstantiationException {
118+ + final Map<String, ParameterProviderReference> parameterProviderReferences, ProcessGroup topLevelGroup) throws ProcessorInstantiationException, FlowSynchronizationException {
119+ final String id = componentIdGenerator.generateUuid(proposed.getIdentifier(), proposed.getInstanceIdentifier(), destination.getIdentifier());
120+ final String connectorId = destination.getConnectorIdentifier().orElse(null);
121+ final ProcessGroup group = context.getFlowManager().createProcessGroup(id, connectorId);
0 commit comments