This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new acf723204ed NIFI-16519 Remove coupling between component scheduling 
and ComponentMetricContext lifecycle (#11859)
acf723204ed is described below

commit acf723204ed2c36cdcd67383d1248487cf3e9fbc
Author: boguszj <[email protected]>
AuthorDate: Thu Nov 5 18:30:30 2026 +0100

    NIFI-16519 Remove coupling between component scheduling and 
ComponentMetricContext lifecycle (#11859)
    
    Signed-off-by: David Handermann <[email protected]>
---
 .../repository/AbstractRepositoryContext.java      |  12 +-
 .../controller/repository/RepositoryContext.java   |   3 -
 .../repository/StandardProcessSession.java         |  26 ++---
 .../java/org/apache/nifi/util/Connectables.java    |   9 ++
 .../repository/StandardProcessSessionTest.java     |  30 +++++
 .../nifi/controller/tasks/ConnectableTask.java     |   4 +-
 .../nifi/controller/tasks/StatelessFlowTask.java   |  19 ++--
 .../repository/StandardRepositoryContextTest.java  | 126 +++++++++++++++++++++
 .../nifi/controller/tasks/TestConnectableTask.java |  46 +++++++-
 .../flow/StandardStatelessFlowCurrent.java         |  10 +-
 .../stateless/session/StatelessProcessSession.java |   5 +-
 11 files changed, 237 insertions(+), 53 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/AbstractRepositoryContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/AbstractRepositoryContext.java
index 29c8d6817e1..03bbc60e47d 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/AbstractRepositoryContext.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/AbstractRepositoryContext.java
@@ -56,7 +56,6 @@ public abstract class AbstractRepositoryContext implements 
RepositoryContext {
     private final ProvenanceEventRepository provenanceRepo;
     private final AtomicLong connectionIndex;
     private final StateManager stateManager;
-    private final ComponentMetricContext componentMetricContext;
     private final ContentClaimCreationContext contentClaimCreationContext;
     private final FlowFileUpdateContext flowFileUpdateContext;
 
@@ -84,11 +83,6 @@ public abstract class AbstractRepositoryContext implements 
RepositoryContext {
 
         this.connectionIndex = connectionIndex;
         this.stateManager = stateManager;
-        this.componentMetricContext = new ComponentMetricContext(
-                connectable.getIdentifier(),
-                connectable.getName(),
-                connectable.getComponentType(),
-                connectable.getProcessGroup().getLoggingAttributes());
 
         this.componentNameCounterContext = connectable.getName() + " (" + 
connectable.getIdentifier() + ")";
         this.componentTypeCounterContext = "All " + 
connectable.getComponentType() + "'s";
@@ -104,11 +98,6 @@ public abstract class AbstractRepositoryContext implements 
RepositoryContext {
         return connectable;
     }
 
-    @Override
-    public ComponentMetricContext getComponentMetricContext() {
-        return componentMetricContext;
-    }
-
     /**
      *
      * @param relationship relationship
@@ -177,6 +166,7 @@ public abstract class AbstractRepositoryContext implements 
RepositoryContext {
         counterRepo.adjustCounter(componentNameCounterContext, name, delta);
         counterRepo.adjustCounter(componentTypeCounterContext, name, delta);
 
+        final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(connectable);
         final CounterRecord counterRecord = new CounterRecord(name, delta, 
attributes, Instant.now(), componentMetricContext);
         componentMetricReporter.recordCounter(counterRecord);
     }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/RepositoryContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/RepositoryContext.java
index 30cdc53caa5..8f5d50cf81c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/RepositoryContext.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/RepositoryContext.java
@@ -20,7 +20,6 @@ package org.apache.nifi.controller.repository;
 import org.apache.nifi.components.state.StateManager;
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
-import org.apache.nifi.controller.metrics.ComponentMetricContext;
 import org.apache.nifi.controller.metrics.ConnectionStatusEvent;
 import org.apache.nifi.controller.metrics.GaugeRecord;
 import org.apache.nifi.controller.metrics.ProcessSessionEvent;
@@ -40,8 +39,6 @@ import java.util.function.Predicate;
 public interface RepositoryContext {
     Connectable getConnectable();
 
-    ComponentMetricContext getComponentMetricContext();
-
     Collection<Connection> getConnections(Relationship relationship);
 
     List<Connection> getPollableConnections();
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/StandardProcessSession.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/StandardProcessSession.java
index 1175b636d44..561c25d79b5 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/StandardProcessSession.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/controller/repository/StandardProcessSession.java
@@ -52,7 +52,6 @@ import org.apache.nifi.controller.status.FlowFileAvailability;
 import org.apache.nifi.controller.status.LoadBalanceStatus;
 import org.apache.nifi.flowfile.FlowFile;
 import org.apache.nifi.flowfile.attributes.CoreAttributes;
-import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.processor.DataUnit;
 import org.apache.nifi.processor.FlowFileFilter;
 import org.apache.nifi.processor.ProcessSession;
@@ -77,6 +76,7 @@ import org.apache.nifi.stream.io.ByteCountingOutputStream;
 import org.apache.nifi.stream.io.LimitingInputStream;
 import org.apache.nifi.stream.io.NonFlushableOutputStream;
 import org.apache.nifi.stream.io.StreamUtils;
+import org.apache.nifi.util.Connectables;
 import org.apache.nifi.util.FormatUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -795,7 +795,8 @@ public class StandardProcessSession implements 
ProcessSession, ProvenanceEventEn
 
             final Map<String, Long> counters = 
combineCounters(checkpoint.countersOnCommit, checkpoint.immediateCounters);
 
-            final ProcessSessionEvent flowFileEvent = 
ProcessSessionEventBuilder.forComponent(context.getComponentMetricContext())
+            final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(context.getConnectable());
+            final ProcessSessionEvent flowFileEvent = 
ProcessSessionEventBuilder.forComponent(componentMetricContext)
                     .flowFilesIn(checkpoint.flowFilesIn)
                     .flowFilesOut(checkpoint.flowFilesOut)
                     .flowFilesRemoved(checkpoint.removedCount)
@@ -833,12 +834,12 @@ public class StandardProcessSession implements 
ProcessSession, ProvenanceEventEn
             for (final Connection connection : 
checkpoint.processedConnections.values()) {
                 final Connectable source = connection.getSource();
                 final ComponentMetricContext sourceContext = 
connectableMetricContexts.computeIfAbsent(source.getIdentifier(),
-                    id -> getComponentMetricContext(source)
+                    id -> Connectables.createComponentMetricContext(source)
                 );
 
                 final Connectable destination = connection.getDestination();
                 final ComponentMetricContext destinationContext = 
connectableMetricContexts.computeIfAbsent(destination.getIdentifier(),
-                        id -> getComponentMetricContext(destination)
+                        id -> 
Connectables.createComponentMetricContext(destination)
                 );
 
                 final ComponentMetricContext connectionMetricContext = 
checkpoint.connectionMetricContexts.get(connection.getIdentifier());
@@ -865,17 +866,6 @@ public class StandardProcessSession implements 
ProcessSession, ProvenanceEventEn
         }
     }
 
-    private ComponentMetricContext getComponentMetricContext(final Connectable 
connectable) {
-        final ProcessGroup processGroup = connectable.getProcessGroup();
-        final Map<String, String> attributes = processGroup == null ? Map.of() 
: processGroup.getLoggingAttributes();
-        return new ComponentMetricContext(
-                connectable.getIdentifier(),
-                connectable.getName(),
-                connectable.getComponentType(),
-                attributes
-        );
-    }
-
     private LoadBalanceStatus getLoadBalanceStatus(final FlowFileQueue 
flowFileQueue) {
         final LoadBalanceStatus loadBalanceStatus;
 
@@ -1422,7 +1412,8 @@ public class StandardProcessSession implements 
ProcessSession, ProvenanceEventEn
             }
         }
 
-        final ProcessSessionEvent flowFileEvent = 
ProcessSessionEventBuilder.forComponent(context.getComponentMetricContext())
+        final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(context.getConnectable());
+        final ProcessSessionEvent flowFileEvent = 
ProcessSessionEventBuilder.forComponent(componentMetricContext)
                 .bytesRead(bytesRead)
                 .bytesWritten(bytesWritten)
                 .counters(getCounterValues(immediateCounters))
@@ -2035,7 +2026,8 @@ public class StandardProcessSession implements 
ProcessSession, ProvenanceEventEn
         Objects.requireNonNull(commitTiming, "Commit Timing required");
 
         final Instant recorded = Instant.now();
-        final GaugeRecord gaugeRecord = new GaugeRecord(name, value, 
Map.copyOf(attributes), recorded, context.getComponentMetricContext());
+        final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(context.getConnectable());
+        final GaugeRecord gaugeRecord = new GaugeRecord(name, value, 
Map.copyOf(attributes), recorded, componentMetricContext);
 
         if (CommitTiming.NOW == commitTiming) {
             context.recordGauge(gaugeRecord);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/Connectables.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/Connectables.java
index 4a36be5a85c..6fe1bb33c0c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/Connectables.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/util/Connectables.java
@@ -18,11 +18,14 @@ package org.apache.nifi.util;
 
 import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
+import org.apache.nifi.controller.metrics.ComponentMetricContext;
 import org.apache.nifi.controller.status.FlowFileAvailability;
+import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.processor.Relationship;
 
 import java.util.Collection;
 import java.util.List;
+import java.util.Map;
 
 public class Connectables {
 
@@ -66,4 +69,10 @@ public class Connectables {
 
         return false;
     }
+
+    public static ComponentMetricContext createComponentMetricContext(final 
Connectable connectable) {
+        final ProcessGroup processGroup = connectable.getProcessGroup();
+        final Map<String, String> groupAttributes = processGroup == null ? 
Map.of() : processGroup.getLoggingAttributes();
+        return new ComponentMetricContext(connectable.getIdentifier(), 
connectable.getName(), connectable.getComponentType(), groupAttributes);
+    }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/controller/repository/StandardProcessSessionTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/controller/repository/StandardProcessSessionTest.java
index ad04a8ecfd6..f4b4001795a 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/controller/repository/StandardProcessSessionTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/test/java/org/apache/nifi/controller/repository/StandardProcessSessionTest.java
@@ -33,6 +33,7 @@ import 
org.apache.nifi.controller.repository.metrics.PerformanceTracker;
 import org.apache.nifi.controller.status.FlowFileAvailability;
 import org.apache.nifi.controller.status.LoadBalanceStatus;
 import org.apache.nifi.flowfile.FlowFile;
+import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.processor.ProcessSession;
 import org.apache.nifi.processor.Relationship;
 import org.apache.nifi.processor.metrics.CommitTiming;
@@ -99,6 +100,10 @@ class StandardProcessSessionTest {
 
     private static final Map<String, String> METRIC_ATTRIBUTES = 
Map.of("service.name", "Processing", "deployment.environment", "production");
 
+    private static final Map<String, String> INITIAL_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:1.0.0");
+
+    private static final Map<String, String> UPDATED_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:2.0.0");
+
     private static final String INPUT_CONNECTION_ID = "input-connection-id";
     private static final String OUTPUT_CONNECTION_ID = "output-connection-id";
     private static final String BACK_PRESSURE_DATA_SIZE_THRESHOLD = "1 MB";
@@ -391,6 +396,31 @@ class StandardProcessSessionTest {
         assertGaugeRecordsMatched();
     }
 
+    @Test
+    void testCommitRecordsCurrentProcessGroupLoggingAttributes() {
+        setRepositoryContext();
+        final ProcessGroup processGroup = mock(ProcessGroup.class);
+        when(connectable.getProcessGroup()).thenReturn(processGroup);
+        
when(processGroup.getLoggingAttributes()).thenReturn(INITIAL_LOGGING_ATTRIBUTES);
+
+        session.recordGauge(GAUGE_NAME, GAUGE_VALUE, 
CommitTiming.SESSION_COMMITTED);
+        session.commit();
+
+        
when(processGroup.getLoggingAttributes()).thenReturn(UPDATED_LOGGING_ATTRIBUTES);
+        session.recordGauge(GAUGE_NAME, GAUGE_VALUE, 
CommitTiming.SESSION_COMMITTED);
+        session.commit();
+
+        verify(repositoryContext, 
times(2)).recordGauge(gaugeRecordCaptor.capture());
+        final List<GaugeRecord> gaugeRecords = 
gaugeRecordCaptor.getAllValues();
+        assertEquals(INITIAL_LOGGING_ATTRIBUTES, 
gaugeRecords.getFirst().componentMetricContext().attributes());
+        assertEquals(UPDATED_LOGGING_ATTRIBUTES, 
gaugeRecords.getLast().componentMetricContext().attributes());
+
+        verify(repositoryContext, 
times(2)).recordProcessSessionEvent(processSessionEventCaptor.capture());
+        final List<ProcessSessionEvent> processSessionEvents = 
processSessionEventCaptor.getAllValues();
+        assertEquals(INITIAL_LOGGING_ATTRIBUTES, 
processSessionEvents.getFirst().getComponentMetricContext().attributes());
+        assertEquals(UPDATED_LOGGING_ATTRIBUTES, 
processSessionEvents.getLast().getComponentMetricContext().attributes());
+    }
+
     @Test
     void testAdjustCounterNow() {
         session.adjustCounter(COUNTER_NAME, COUNTER_DELTA, true);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ConnectableTask.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ConnectableTask.java
index 7c94e8a6163..7143ec9c298 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ConnectableTask.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/ConnectableTask.java
@@ -24,6 +24,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.lifecycle.TaskTerminationAwareStateManager;
+import org.apache.nifi.controller.metrics.ComponentMetricContext;
 import org.apache.nifi.controller.metrics.ProcessSessionEvent;
 import org.apache.nifi.controller.repository.ActiveProcessSessionFactory;
 import org.apache.nifi.controller.repository.BatchingSessionFactory;
@@ -321,7 +322,8 @@ public class ConnectableTask {
     }
 
     private void updateEventRepo(final TrackedStats stats, final int 
invocationCount) throws IOException {
-        final ProcessSessionEvent flowFileEvent = 
stats.end(repositoryContext.getComponentMetricContext())
+        final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(connectable);
+        final ProcessSessionEvent flowFileEvent = 
stats.end(componentMetricContext)
                 .invocations(invocationCount)
                 .build();
         
repositoryContext.getFlowFileEventRepository().updateRepository(flowFileEvent);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/StatelessFlowTask.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/StatelessFlowTask.java
index deae10e4ff2..40161d1bad3 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/StatelessFlowTask.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/tasks/StatelessFlowTask.java
@@ -17,7 +17,6 @@
 
 package org.apache.nifi.controller.tasks;
 
-import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.ConnectionUtils;
 import org.apache.nifi.connectable.ConnectionUtils.FlowFileCloneResult;
@@ -25,7 +24,6 @@ import org.apache.nifi.connectable.Port;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
 import org.apache.nifi.controller.Triggerable;
-import org.apache.nifi.controller.metrics.ComponentMetricContext;
 import org.apache.nifi.controller.queue.FlowFileQueue;
 import org.apache.nifi.controller.repository.ContentRepository;
 import org.apache.nifi.controller.repository.FlowFileEventRepository;
@@ -57,6 +55,7 @@ import org.apache.nifi.stateless.flow.FlowFileSupplier;
 import org.apache.nifi.stateless.flow.StatelessDataflow;
 import org.apache.nifi.stateless.flow.TriggerResult;
 import org.apache.nifi.stateless.repository.StatelessProvenanceRepository;
+import org.apache.nifi.util.Connectables;
 
 import java.io.IOException;
 import java.time.Duration;
@@ -632,7 +631,9 @@ public class StatelessFlowTask {
 
                 final Port inputPort = polledFlowFile.getInputPort();
                 final ProcessSessionEventBuilder cumulativeEvent = 
eventsByComponentId.computeIfAbsent(
-                        inputPort.getIdentifier(), id -> 
ProcessSessionEventBuilder.forComponent(createMetricContext(inputPort)));
+                    inputPort.getIdentifier(),
+                    id -> 
ProcessSessionEventBuilder.forComponent(Connectables.createComponentMetricContext(inputPort))
+                );
                 cumulativeEvent.addFlowFilesIn(1)
                         .addFlowFilesOut(numOutputConnections)
                         .addContentSizeIn(bytes)
@@ -654,7 +655,9 @@ public class StatelessFlowTask {
                 final int outputConnectionCount = port.getConnections().size();
 
                 final ProcessSessionEventBuilder cumulativeEvent = 
eventsByComponentId.computeIfAbsent(
-                        port.getIdentifier(), id -> 
ProcessSessionEventBuilder.forComponent(createMetricContext(port)));
+                    port.getIdentifier(),
+                    id -> 
ProcessSessionEventBuilder.forComponent(Connectables.createComponentMetricContext(port))
+                );
                 cumulativeEvent.addFlowFilesIn(flowFileCount)
                         .addFlowFilesOut(flowFileCount * outputConnectionCount)
                         .addContentSizeIn(byteCount)
@@ -671,14 +674,6 @@ public class StatelessFlowTask {
         }
     }
 
-    private static ComponentMetricContext createMetricContext(final 
Connectable connectable) {
-        final ProcessGroup processGroup = connectable.getProcessGroup();
-        final Map<String, String> groupAttributes = processGroup == null ? 
Map.of() : processGroup.getLoggingAttributes();
-        return new ComponentMetricContext(
-                connectable.getIdentifier(), connectable.getName(), 
connectable.getComponentType(), groupAttributes);
-    }
-
-
     /**
      * Updates the Stateless Group Node's FlowFileActivity. The latest 
activity time is obtained from the stateless flow's root group,
      * which is updated by processors within the flow when their sessions 
commit. Transfer counts are computed from provenance events
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/StandardRepositoryContextTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/StandardRepositoryContextTest.java
new file mode 100644
index 00000000000..fa36e51703f
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/StandardRepositoryContextTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.controller.repository;
+
+import org.apache.nifi.components.state.StateManager;
+import org.apache.nifi.connectable.Connectable;
+import org.apache.nifi.controller.metrics.ComponentMetricContext;
+import org.apache.nifi.controller.metrics.ComponentMetricReporter;
+import org.apache.nifi.controller.metrics.CounterRecord;
+import org.apache.nifi.groups.ProcessGroup;
+import org.apache.nifi.provenance.ProvenanceEventRepository;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class StandardRepositoryContextTest {
+    private static final String COMPONENT_ID = "component-id";
+
+    private static final String COMPONENT_NAME = "Component Name";
+
+    private static final String COMPONENT_TYPE = "ComponentType";
+
+    private static final String COUNTER_NAME = "records.ingested";
+
+    private static final long COUNTER_DELTA = 5;
+
+    private static final Map<String, String> COUNTER_ATTRIBUTES = 
Map.of("table", "orders");
+
+    private static final Map<String, String> INITIAL_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:1.0.0");
+
+    private static final Map<String, String> UPDATED_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:2.0.0");
+
+    @Mock
+    private Connectable connectable;
+
+    @Mock
+    private ProcessGroup processGroup;
+
+    @Mock
+    private ContentRepository contentRepository;
+
+    @Mock
+    private FlowFileRepository flowFileRepository;
+
+    @Mock
+    private FlowFileEventRepository flowFileEventRepository;
+
+    @Mock
+    private CounterRepository counterRepository;
+
+    @Mock
+    private ComponentMetricReporter componentMetricReporter;
+
+    @Mock
+    private ProvenanceEventRepository provenanceEventRepository;
+
+    @Mock
+    private StateManager stateManager;
+
+    @Captor
+    private ArgumentCaptor<CounterRecord> counterRecordCaptor;
+
+    @Test
+    void testAdjustCounterRecordsCurrentProcessGroupLoggingAttributes() {
+        when(connectable.getIdentifier()).thenReturn(COMPONENT_ID);
+        when(connectable.getName()).thenReturn(COMPONENT_NAME);
+        when(connectable.getComponentType()).thenReturn(COMPONENT_TYPE);
+        when(connectable.getProcessGroup()).thenReturn(processGroup);
+        
when(processGroup.getLoggingAttributes()).thenReturn(INITIAL_LOGGING_ATTRIBUTES);
+
+        final StandardRepositoryContext repositoryContext = new 
StandardRepositoryContext(
+            connectable,
+            new AtomicLong(),
+            contentRepository,
+            flowFileRepository,
+            flowFileEventRepository,
+            counterRepository,
+            componentMetricReporter,
+            provenanceEventRepository,
+            stateManager
+        );
+        repositoryContext.adjustCounter(COUNTER_NAME, COUNTER_DELTA, 
COUNTER_ATTRIBUTES);
+
+        
when(processGroup.getLoggingAttributes()).thenReturn(UPDATED_LOGGING_ATTRIBUTES);
+        repositoryContext.adjustCounter(COUNTER_NAME, COUNTER_DELTA, 
COUNTER_ATTRIBUTES);
+
+        verify(componentMetricReporter, 
times(2)).recordCounter(counterRecordCaptor.capture());
+        final List<CounterRecord> counterRecords = 
counterRecordCaptor.getAllValues();
+        assertCounterRecordEquals(INITIAL_LOGGING_ATTRIBUTES, 
counterRecords.getFirst());
+        assertCounterRecordEquals(UPDATED_LOGGING_ATTRIBUTES, 
counterRecords.getLast());
+    }
+
+    private void assertCounterRecordEquals(final Map<String, String> 
expectedLoggingAttributes, final CounterRecord counterRecord) {
+        assertEquals(COUNTER_NAME, counterRecord.name());
+        assertEquals(COUNTER_DELTA, counterRecord.value());
+        assertEquals(COUNTER_ATTRIBUTES, counterRecord.attributes());
+        assertEquals(new ComponentMetricContext(COMPONENT_ID, COMPONENT_NAME, 
COMPONENT_TYPE, expectedLoggingAttributes), 
counterRecord.componentMetricContext());
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/tasks/TestConnectableTask.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/tasks/TestConnectableTask.java
index a41da4b0c9a..ee6b1d4930c 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/tasks/TestConnectableTask.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/tasks/TestConnectableTask.java
@@ -26,6 +26,7 @@ import org.apache.nifi.controller.FlowController;
 import org.apache.nifi.controller.GarbageCollectionLog;
 import org.apache.nifi.controller.ProcessorNode;
 import org.apache.nifi.controller.ScheduledState;
+import org.apache.nifi.controller.metrics.ProcessSessionEvent;
 import org.apache.nifi.controller.queue.FlowFileQueue;
 import org.apache.nifi.controller.repository.FlowFileEventRepository;
 import org.apache.nifi.controller.repository.RepositoryContext;
@@ -34,34 +35,50 @@ import org.apache.nifi.controller.scheduling.LifecycleState;
 import org.apache.nifi.controller.scheduling.RepositoryContextFactory;
 import org.apache.nifi.controller.scheduling.SchedulingAgent;
 import org.apache.nifi.controller.status.FlowFileAvailability;
+import org.apache.nifi.groups.ProcessGroup;
 import org.apache.nifi.nar.ExtensionManager;
 import org.apache.nifi.processor.Processor;
 import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.AtomicReference;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class TestConnectableTask {
 
+    private static final Map<String, String> INITIAL_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:1.0.0");
+
+    private static final Map<String, String> UPDATED_LOGGING_ATTRIBUTES = 
Map.of("registeredFlowIdentifierPath", "/root:1/connector:2.0.0");
+
     private ConnectableTask createTask(final Connectable connectable) {
+        return createTask(connectable, mock(FlowFileEventRepository.class));
+    }
+
+    private ConnectableTask createTask(final Connectable connectable, final 
FlowFileEventRepository flowFileEventRepository) {
         final FlowController flowController = mock(FlowController.class);
         
when(flowController.getStateManagerProvider()).thenReturn(mock(StateManagerProvider.class));
         
when(flowController.getExtensionManager()).thenReturn(mock(ExtensionManager.class));
 
         final RepositoryContext repoContext = 
mock(StandardRepositoryContext.class);
-        
when(repoContext.getFlowFileEventRepository()).thenReturn(mock(FlowFileEventRepository.class));
+        
when(repoContext.getFlowFileEventRepository()).thenReturn(flowFileEventRepository);
 
         
when(flowController.getGarbageCollectionLog()).thenReturn(mock(GarbageCollectionLog.class));
 
@@ -102,6 +119,33 @@ public class TestConnectableTask {
         assertFalse(taskMonitorHeld.get());
     }
 
+    @Test
+    public void testInvokeRecordsCurrentProcessGroupLoggingAttributes() throws 
IOException {
+        final ProcessGroup processGroup = mock(ProcessGroup.class);
+        
when(processGroup.getLoggingAttributes()).thenReturn(INITIAL_LOGGING_ATTRIBUTES);
+
+        final ProcessorNode processorNode = mock(ProcessorNode.class);
+        when(processorNode.getIdentifier()).thenReturn("processor-id");
+        
when(processorNode.getRunnableComponent()).thenReturn(mock(Processor.class));
+        
when(processorNode.getRelationships()).thenReturn(Collections.emptySet());
+        
when(processorNode.getIncomingConnections()).thenReturn(Collections.emptyList());
+        
when(processorNode.getScheduledState()).thenReturn(ScheduledState.RUNNING);
+        when(processorNode.getProcessGroup()).thenReturn(processGroup);
+
+        final FlowFileEventRepository flowFileEventRepository = 
mock(FlowFileEventRepository.class);
+        final ConnectableTask task = createTask(processorNode, 
flowFileEventRepository);
+        task.invoke();
+
+        
when(processGroup.getLoggingAttributes()).thenReturn(UPDATED_LOGGING_ATTRIBUTES);
+        task.invoke();
+
+        final ArgumentCaptor<ProcessSessionEvent> eventCaptor = 
ArgumentCaptor.forClass(ProcessSessionEvent.class);
+        verify(flowFileEventRepository, 
times(2)).updateRepository(eventCaptor.capture());
+        final List<ProcessSessionEvent> events = eventCaptor.getAllValues();
+        assertEquals(INITIAL_LOGGING_ATTRIBUTES, 
events.getFirst().getComponentMetricContext().attributes());
+        assertEquals(UPDATED_LOGGING_ATTRIBUTES, 
events.getLast().getComponentMetricContext().attributes());
+    }
+
     @Test
     public void testIsWorkToDo() {
         final ProcessorNode procNode = mock(ProcessorNode.class);
diff --git 
a/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/flow/StandardStatelessFlowCurrent.java
 
b/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/flow/StandardStatelessFlowCurrent.java
index 0c8a094917d..a2e10521877 100644
--- 
a/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/flow/StandardStatelessFlowCurrent.java
+++ 
b/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/flow/StandardStatelessFlowCurrent.java
@@ -40,12 +40,12 @@ import 
org.apache.nifi.stateless.engine.ProcessContextFactory;
 import org.apache.nifi.stateless.repository.RepositoryContextFactory;
 import org.apache.nifi.stateless.session.AsynchronousCommitTracker;
 import org.apache.nifi.stateless.session.StatelessProcessSessionFactory;
+import org.apache.nifi.util.Connectables;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
 import java.util.Collection;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -313,12 +313,8 @@ public class StandardStatelessFlowCurrent implements 
StatelessFlowCurrent {
 
     private void registerProcessEvent(final Connectable connectable, final 
TrackedStats trackedStats) {
         try {
-            final Map<String, String> groupAttributes = 
connectable.getProcessGroup() == null
-                    ? Map.of()
-                    : connectable.getProcessGroup().getLoggingAttributes();
-            final ComponentMetricContext metricContext = new 
ComponentMetricContext(
-                    connectable.getIdentifier(), connectable.getName(), 
connectable.getComponentType(), groupAttributes);
-            final ProcessSessionEvent procEvent = 
trackedStats.end(metricContext)
+            final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(connectable);
+            final ProcessSessionEvent procEvent = 
trackedStats.end(componentMetricContext)
                     .invocations(1)
                     .build();
             
repositoryContextFactory.getFlowFileEventRepository().updateRepository(procEvent);
diff --git 
a/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/session/StatelessProcessSession.java
 
b/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/session/StatelessProcessSession.java
index 0434bdebda3..efdcf1f3637 100644
--- 
a/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/session/StatelessProcessSession.java
+++ 
b/nifi-stateless/nifi-stateless-bundle/nifi-stateless-engine/src/main/java/org/apache/nifi/stateless/session/StatelessProcessSession.java
@@ -22,6 +22,7 @@ import org.apache.nifi.connectable.Connectable;
 import org.apache.nifi.connectable.ConnectableType;
 import org.apache.nifi.connectable.Connection;
 import org.apache.nifi.connectable.Port;
+import org.apache.nifi.controller.metrics.ComponentMetricContext;
 import org.apache.nifi.controller.metrics.ProcessSessionEvent;
 import org.apache.nifi.controller.repository.FlowFileRecord;
 import org.apache.nifi.controller.repository.StandardProcessSession;
@@ -38,6 +39,7 @@ import 
org.apache.nifi.stateless.flow.FailurePortEncounteredException;
 import org.apache.nifi.stateless.flow.StandardStatelessFlow;
 import org.apache.nifi.stateless.queue.DrainableFlowFileQueue;
 import org.apache.nifi.stateless.repository.RepositoryContextFactory;
+import org.apache.nifi.util.Connectables;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -347,7 +349,8 @@ public class StatelessProcessSession extends 
StandardProcessSession {
 
     private void registerProcessEvent(final Connectable connectable, final 
long processingNanos) {
         try {
-            final ProcessSessionEvent procEvent = 
ProcessSessionEventBuilder.forComponent(getRepositoryContext().getComponentMetricContext())
+            final ComponentMetricContext componentMetricContext = 
Connectables.createComponentMetricContext(connectable);
+            final ProcessSessionEvent procEvent = 
ProcessSessionEventBuilder.forComponent(componentMetricContext)
                     .invocations(1)
                     .processingNanoseconds(processingNanos)
                     .build();

Reply via email to