diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
index 047c5810fb3c..3dce83e0ed7e 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml
@@ -22,8 +22,6 @@
nifi-standard-rules
jar
-
-
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java
new file mode 100644
index 000000000000..28f30765ac88
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java
@@ -0,0 +1,254 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules;
+
+import org.apache.nifi.annotation.documentation.CapabilityDescription;
+import org.apache.nifi.annotation.documentation.Tags;
+import org.apache.nifi.components.AllowableValue;
+import org.apache.nifi.components.PropertyDescriptor;
+import org.apache.nifi.components.ValidationContext;
+import org.apache.nifi.components.ValidationResult;
+import org.apache.nifi.flow.VersionedProcessGroup;
+import org.apache.nifi.flowanalysis.AbstractFlowAnalysisRule;
+import org.apache.nifi.flowanalysis.FlowAnalysisRuleContext;
+import org.apache.nifi.flowanalysis.GroupAnalysisResult;
+import org.apache.nifi.flowanalysis.rules.util.ConnectionViolation;
+import org.apache.nifi.flowanalysis.rules.util.FlowAnalysisRuleUtils;
+import org.apache.nifi.flowanalysis.rules.util.ViolationType;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+@Tags({"connection", "load", "balance", "balancing", "strategy", "compress"})
+@CapabilityDescription("This rule generates a violation when the Load Balance Strategy setting of a connection is set to a strategy which is not allowed. "
+ + "This rule may be useful for preventing certain strategy types or load-balancing compression. It may be implemented to assist in locating "
+ + "all connections which have load balancing enabled.")
+public class RestrictLoadBalancing extends AbstractFlowAnalysisRule {
+
+ static final String ALLOWED = "Allowed";
+ static final String DISALLOWED = "Disallowed";
+ static final AllowableValue ALLOWABLE_ALLOWED = new AllowableValue(ALLOWED, ALLOWED);
+ static final AllowableValue ALLOWABLE_DISALLOWED = new AllowableValue(DISALLOWED, DISALLOWED);
+ static final String CONFIGURE_STRATEGY_ERROR_MESSAGE = "is set to " + ALLOWED + " so at least one load balancing strategy property must be set to " + ALLOWED;
+
+ // The following constants are derived from LoadBalanceStrategy and LoadBalanceCompression enums in the nifi-framework-api.
+ // If values in the API change, the corresponding values here must also be updated.
+ // The API is not referenced directly to avoid a dependency on the framework API.
+ private static final String PARTITION_BY_ATTRIBUTE = "PARTITION_BY_ATTRIBUTE";
+ private static final String ROUND_ROBIN = "ROUND_ROBIN";
+ private static final String SINGLE_NODE = "SINGLE_NODE";
+ private static final String COMPRESS_ATTRIBUTES_ONLY = "COMPRESS_ATTRIBUTES_ONLY";
+ private static final String COMPRESS_ATTRIBUTES_AND_CONTENT = "COMPRESS_ATTRIBUTES_AND_CONTENT";
+
+
+ public static final PropertyDescriptor LOAD_BALANCING_POLICY = new PropertyDescriptor.Builder()
+ .name("Load Balancing Policy")
+ .description("Determines whether any load balance strategy is allowed. In all cases, 'Do not load balance' is valid and will not cause a violation.")
+ .required(true)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(DISALLOWED)
+ .build();
+
+ public static final PropertyDescriptor ALLOW_PARTITION = new PropertyDescriptor.Builder()
+ .name("Partition By Attribute Strategy")
+ .description("Connections may be configured with the load balancing strategy of 'Partition by attribute'.")
+ .required(false)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(ALLOWED)
+ .dependsOn(LOAD_BALANCING_POLICY, ALLOWED)
+ .build();
+
+ public static final PropertyDescriptor ALLOW_ROUND_ROBIN = new PropertyDescriptor.Builder()
+ .name("Round Robin Strategy")
+ .description("Connections may be configured with the load balancing strategy of 'Round robin'.")
+ .required(false)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(ALLOWED)
+ .dependsOn(LOAD_BALANCING_POLICY, ALLOWED)
+ .build();
+
+ public static final PropertyDescriptor ALLOW_SINGLE_NODE = new PropertyDescriptor.Builder()
+ .name("Single Node Strategy")
+ .description("Connections may be configured with the load balancing strategy of 'Single node'.")
+ .required(false)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(ALLOWED)
+ .dependsOn(LOAD_BALANCING_POLICY, ALLOWED)
+ .build();
+
+ public static final PropertyDescriptor ALLOW_ATTRIBUTE_COMPRESSION = new PropertyDescriptor.Builder()
+ .name("Compress Attributes Only")
+ .description("Connections which are configured with a load balancing strategy, may compress attributes.")
+ .required(false)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(ALLOWED)
+ .dependsOn(LOAD_BALANCING_POLICY, ALLOWED)
+ .build();
+
+ public static final PropertyDescriptor ALLOW_CONTENT_COMPRESSION = new PropertyDescriptor.Builder()
+ .name("Compress Attributes and Content")
+ .description("Connections which are configured with a load balancing strategy, may compress both attributes and content.")
+ .required(false)
+ .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED)
+ .defaultValue(ALLOWED)
+ .dependsOn(LOAD_BALANCING_POLICY, ALLOWED)
+ .build();
+
+ private static final List PROPERTY_DESCRIPTORS = List.of(
+ LOAD_BALANCING_POLICY,
+ ALLOW_PARTITION,
+ ALLOW_ROUND_ROBIN,
+ ALLOW_SINGLE_NODE,
+ ALLOW_ATTRIBUTE_COMPRESSION,
+ ALLOW_CONTENT_COMPRESSION
+ );
+
+ @Override
+ protected List getSupportedPropertyDescriptors() {
+ return PROPERTY_DESCRIPTORS;
+ }
+
+ @Override
+ protected Collection customValidate(ValidationContext validationContext) {
+ final List results = new ArrayList<>();
+
+ // For this flow analysis rule to be valid, load balancing must be disallowed, or at least one load balancing strategy must be allowed
+ if (validationContext.getProperty(LOAD_BALANCING_POLICY).getValue().equals(ALLOWED)
+ && !isLoadBalancingStrategyAllowed(validationContext)) {
+ results.add(new ValidationResult.Builder()
+ .subject(LOAD_BALANCING_POLICY.getName())
+ .valid(false)
+ .explanation(LOAD_BALANCING_POLICY.getName() + " " + CONFIGURE_STRATEGY_ERROR_MESSAGE)
+ .build());
+ }
+
+ return results;
+ }
+
+ @Override
+ public Collection analyzeProcessGroup(VersionedProcessGroup processGroup, FlowAnalysisRuleContext context) {
+ final Collection violations = new ArrayList<>();
+ final boolean allowLoadBalancing = context.getProperty(LOAD_BALANCING_POLICY).getValue().equals(ALLOWED);
+ final boolean allowPartition = context.getProperty(ALLOW_PARTITION).getValue().equals(ALLOWED);
+ final boolean allowRoundRobin = context.getProperty(ALLOW_ROUND_ROBIN).getValue().equals(ALLOWED);
+ final boolean allowSingleNode = context.getProperty(ALLOW_SINGLE_NODE).getValue().equals(ALLOWED);
+ final boolean allowAttributesCompression = context.getProperty(ALLOW_ATTRIBUTE_COMPRESSION).getValue().equals(ALLOWED);
+ final boolean allowAttributesAndContentCompression = context.getProperty(ALLOW_CONTENT_COMPRESSION).getValue().equals(ALLOWED);
+
+ processGroup.getConnections().forEach(connection -> {
+ final String strategy = connection.getLoadBalanceStrategy();
+
+ if (isLoadBalancingEnabled(strategy) && !allowLoadBalancing) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.LOAD_BALANCE_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceStrategy(),
+ context.getProperty(LOAD_BALANCING_POLICY).getValue()));
+ }
+ if (PARTITION_BY_ATTRIBUTE.equals(strategy) && !allowPartition) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.PARTITION_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceStrategy(),
+ context.getProperty(ALLOW_PARTITION).getValue()));
+ }
+ if (ROUND_ROBIN.equals(strategy) && !allowRoundRobin) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.ROUND_ROBIN_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceStrategy(),
+ context.getProperty(ALLOW_ROUND_ROBIN).getValue()));
+ }
+ if (SINGLE_NODE.equals(strategy) && !allowSingleNode) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.SINGLE_NODE_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceStrategy(),
+ context.getProperty(ALLOW_SINGLE_NODE).getValue()));
+ }
+
+ // Evaluate compression only if one of the load balance strategies is specified
+ if (isLoadBalancingEnabled(strategy)) {
+ String compression = connection.getLoadBalanceCompression();
+ if (COMPRESS_ATTRIBUTES_ONLY.equals(compression) && !allowAttributesCompression) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.ATTRIBUTE_COMPRESSION_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceCompression(),
+ context.getProperty(ALLOW_ATTRIBUTE_COMPRESSION).getValue()));
+ }
+ if (COMPRESS_ATTRIBUTES_AND_CONTENT.equals(compression) && !allowAttributesAndContentCompression) {
+ violations.add(new ConnectionViolation(connection,
+ LoadBalanceViolationType.CONTENT_COMPRESSION_DISALLOWED,
+ this.getClass().getSimpleName(),
+ connection.getLoadBalanceCompression(),
+ context.getProperty(ALLOW_CONTENT_COMPRESSION).getValue()));
+ }
+ }
+ });
+
+ return FlowAnalysisRuleUtils.convertToGroupAnalysisResults(processGroup, violations);
+ }
+
+ private boolean isLoadBalancingStrategyAllowed(ValidationContext validationContext) {
+ return validationContext.getProperty(ALLOW_PARTITION).getValue().equals(ALLOWED)
+ || validationContext.getProperty(ALLOW_ROUND_ROBIN).getValue().equals(ALLOWED)
+ || validationContext.getProperty(ALLOW_SINGLE_NODE).getValue().equals(ALLOWED);
+ }
+
+ private boolean isLoadBalancingEnabled(String strategy) {
+ return PARTITION_BY_ATTRIBUTE.equals(strategy)
+ || ROUND_ROBIN.equals(strategy)
+ || SINGLE_NODE.equals(strategy);
+ }
+
+ private enum LoadBalanceViolationType implements ViolationType {
+
+ LOAD_BALANCE_DISALLOWED("NoLoadBalanceNotAllowed", "Load Balance Strategy", "is disallowed because \"" + LOAD_BALANCING_POLICY.getName() + "\" is set to "),
+ PARTITION_DISALLOWED("PartitionByAttributeNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_PARTITION.getName() + "\" is set to "),
+ ROUND_ROBIN_DISALLOWED("RoundRobinNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_ROUND_ROBIN.getName() + "\" is set to "),
+ SINGLE_NODE_DISALLOWED("SingleNodeNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_SINGLE_NODE.getName() + "\" is set to "),
+ ATTRIBUTE_COMPRESSION_DISALLOWED("AttributeCompressionNotAllowed", "Load Balance Compression", "is disallowed because \"" + ALLOW_ATTRIBUTE_COMPRESSION.getName() + "\" is set to "),
+ CONTENT_COMPRESSION_DISALLOWED("ContentCompressionNotAllowed", "Load Balance Compression", "is disallowed because \"" + ALLOW_CONTENT_COMPRESSION.getName() + "\" is set to ");
+
+ private final String id;
+ private final String configurationItem;
+ private final String violationMessage;
+
+ LoadBalanceViolationType(String id, String configurationItem, String violationMessage) {
+ this.id = id;
+ this.configurationItem = configurationItem;
+ this.violationMessage = violationMessage;
+ }
+
+ @Override
+ public String getId() {
+ return this.id;
+ }
+
+ @Override
+ public String getConfigurationItem() {
+ return this.configurationItem;
+ }
+
+ @Override
+ public String getViolationMessage() {
+ return this.violationMessage;
+ }
+ }
+}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
index f5f1df7eb4b5..077d954ca308 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule
@@ -16,3 +16,4 @@
org.apache.nifi.flowanalysis.rules.DisallowComponentType
org.apache.nifi.flowanalysis.rules.RestrictBackpressureSettings
org.apache.nifi.flowanalysis.rules.RestrictFlowFileExpiration
+org.apache.nifi.flowanalysis.rules.RestrictLoadBalancing
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java
index 04b2d38acbd5..5ada34aa608e 100644
--- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java
@@ -85,6 +85,7 @@ private VersionedProcessGroup getProcessGroup(String flowDefinition) throws Exce
protected void testAnalyzeProcessGroup(String flowDefinition, List expected) throws Exception {
final Collection actual = rule.analyzeProcessGroup(getProcessGroup(flowDefinition), flowAnalysisRuleContext);
- assertIterableEquals(expected, actual.stream().map(r -> r.getComponent().get().getInstanceIdentifier()).sorted().toList());
+ assertIterableEquals(expected.stream().sorted().toList(),
+ actual.stream().map(r -> r.getComponent().get().getInstanceIdentifier()).sorted().toList());
}
}
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java
new file mode 100644
index 000000000000..ee8ee1b5863d
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.nifi.flowanalysis.rules;
+
+import org.apache.nifi.components.ValidationResult;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class RestrictLoadBalancingTest extends AbstractFlowAnalaysisRuleTest {
+ // The flow consists of six upstream GenerateFlowFile processors each having one connection to an UpdateAttribute processor
+ // The UUID of the upstream processor is specified in the following variables which describe the properties of the connection
+ // The sixth upstream processor has a connection with 'Do not load balance'; it can never generate a violation
+ private static final String UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID = "2f3f4270-9703-331a-659c-1c583972547b";
+ private static final String UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID = "930d2d12-fefe-39d2-4884-fb9d9330d892";
+ private static final String UPSTREAM_PROCESSOR_SINGLE_NODE_UUID = "aa0e758e-0194-1000-a703-6b124a357ce2";
+ private static final String UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID = "78a971d6-3015-3fc1-17c5-68b7be982aa2";
+ private static final String UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID = "8792e315-8937-300c-ccbd-3130ceca40fc";
+
+ @Override
+ protected RestrictLoadBalancing initializeRule() {
+ return new RestrictLoadBalancing();
+ }
+
+ @BeforeEach
+ @Override
+ public void setup() {
+ super.setup();
+ // Non-default setting, but appropriate for unit tests
+ setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.ALLOWED);
+ // Default settings
+ setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.ALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.ALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.ALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.ALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.ALLOWED);
+ }
+
+ @Test
+ public void testBadConfiguration() {
+ // Set all load balancing strategies to 'Disallowed'
+ setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.DISALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.DISALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.DISALLOWED);
+ ArrayList validationErrors = new ArrayList<>(rule.customValidate(validationContext));
+ assertEquals(1, validationErrors.size());
+ assertTrue(validationErrors.getFirst().getExplanation().contains(RestrictLoadBalancing.CONFIGURE_STRATEGY_ERROR_MESSAGE));
+ }
+
+ @Test
+ public void testGoodConfiguration() {
+ // Set load balancing policy to disallowed while strategies and compression are also allowed
+ setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.DISALLOWED);
+ ArrayList validationErrors = new ArrayList<>(rule.customValidate(validationContext));
+ assertEquals(0, validationErrors.size());
+ }
+
+ @Test
+ public void testNoViolations() throws Exception {
+ // Using unit test defaults, all load balancing configurations are allowed
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of()
+ );
+ }
+
+ @Test
+ public void testViolationsLoadBalancePolicy() throws Exception {
+ setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.DISALLOWED);
+ // Using unit test defaults, all load balancing configurations are allowed
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID,
+ UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID,
+ UPSTREAM_PROCESSOR_SINGLE_NODE_UUID,
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID,
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testViolationRoundRobin() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.DISALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ // Compression connections are also configured with Round robin strategy
+ UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID,
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID,
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testViolationSingleNode() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.DISALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ UPSTREAM_PROCESSOR_SINGLE_NODE_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testViolationPartition() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.DISALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testViolationAttributeCompression() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.DISALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.DISALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID,
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testViolationContentCompression() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.DISALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of(
+ UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID
+ )
+ );
+ }
+
+ @Test
+ public void testNoViolationContentCompression() throws Exception {
+ setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.ALLOWED);
+ setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.ALLOWED);
+ testAnalyzeProcessGroup(
+ "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json",
+ List.of()
+ );
+ }
+}
\ No newline at end of file
diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json
new file mode 100644
index 000000000000..5e78e49cdc4d
--- /dev/null
+++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json
@@ -0,0 +1 @@
+{"flowContents":{"identifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29","instanceIdentifier":"d1fd0a0b-0194-1000-7312-766249d205c0","name":"RestrictLoadBalancing","comments":"","position":{"x":-176.0,"y":-160.0},"processGroups":[],"remoteProcessGroups":[],"processors":[{"identifier":"d2521342-0194-1000-350f-654464c43ba6","instanceIdentifier":"8f321b49-577c-38d5-ffe0-6ee325ddf030","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-568.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d252f44b-0194-1000-d380-6c9cb17885ec","instanceIdentifier":"930d2d12-fefe-39d2-4884-fb9d9330d892","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-288.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d252814b-0194-1000-c644-753fe971fef5","instanceIdentifier":"2f3f4270-9703-331a-659c-1c583972547b","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-432.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"41493fc8-8750-31fd-a4d4-e97a139ff400","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c","name":"UpdateAttribute","comments":"","position":{"x":416.0,"y":-224.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.2.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"901df113-9ade-38f0-96bd-92436b5881b7","instanceIdentifier":"aa0e758e-0194-1000-a703-6b124a357ce2","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-152.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d23d54f3-0194-1000-0049-920ac8d1f9ca","instanceIdentifier":"78a971d6-3015-3fc1-17c5-68b7be982aa2","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-16.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d23d5b3e-0194-1000-a180-c3c4435e457d","instanceIdentifier":"8792e315-8937-300c-ccbd-3130ceca40fc","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":120.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"}],"inputPorts":[],"outputPorts":[],"connections":[{"identifier":"f4bb6f79-eb85-32ee-8a1d-574014b36523","instanceIdentifier":"d25342d7-0194-1000-0c3f-8253ceeb017f","name":"round-robin","source":{"id":"d252f44b-0194-1000-d380-6c9cb17885ec","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"930d2d12-fefe-39d2-4884-fb9d9330d892"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"2928bd29-f2d9-3e91-93a2-96452008c6ce","instanceIdentifier":"d2525cb9-0194-1000-1bb2-8d5434847677","name":"do-not-load-balance","source":{"id":"d2521342-0194-1000-350f-654464c43ba6","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"8f321b49-577c-38d5-ffe0-6ee325ddf030"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"65090306-dfe2-3580-b920-d55503b4f4dd","instanceIdentifier":"d23e3c2b-0194-1000-b467-6ea5edfc3ac3","name":"robin-robin_compress-attributes","source":{"id":"d23d54f3-0194-1000-0049-920ac8d1f9ca","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"78a971d6-3015-3fc1-17c5-68b7be982aa2"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"COMPRESS_ATTRIBUTES_ONLY","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"215fdff4-0f21-31dc-919a-3da83df392f2","instanceIdentifier":"d23e8d64-0194-1000-9edb-e0934db20d0e","name":"round-robin_compress-attributes-and-content","source":{"id":"d23d5b3e-0194-1000-a180-c3c4435e457d","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"8792e315-8937-300c-ccbd-3130ceca40fc"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"COMPRESS_ATTRIBUTES_AND_CONTENT","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"c24e03a8-8903-336d-ba48-92b807af5291","instanceIdentifier":"d252d485-0194-1000-3fcf-48e42aa3ef8c","name":"partition-by-attribute","source":{"id":"d252814b-0194-1000-c644-753fe971fef5","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"2f3f4270-9703-331a-659c-1c583972547b"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"PARTITION_BY_ATTRIBUTE","partitioningAttribute":"foo","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"e28059a2-46e1-3b4a-afa3-e1c65419662e","instanceIdentifier":"d1f99f32-0194-1000-a03c-f706654810a5","name":"single-node","source":{"id":"901df113-9ade-38f0-96bd-92436b5881b7","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"aa0e758e-0194-1000-a703-6b124a357ce2"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"SINGLE_NODE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"}],"labels":[],"funnels":[],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","flowFileConcurrency":"UNBOUNDED","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","componentType":"PROCESS_GROUP"},"externalControllerServices":{},"parameterContexts":{},"flowEncodingVersion":"1.0","parameterProviders":{},"latest":false}
\ No newline at end of file