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

JiaLiangC pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new fc07b5cb87 AMBARI-26661. Refresh telemetry assignments on component 
state transitions (#4222)
fc07b5cb87 is described below

commit fc07b5cb873dc154c48349085494e664baf6b9d1
Author: Jefferson Almeida <[email protected]>
AuthorDate: Sat Sep 19 22:24:28 2026 -0300

    AMBARI-26661. Refresh telemetry assignments on component state transitions 
(#4222)
    
    TelemetryHolder only refreshes a host's telemetry assignment in response to
    ServiceComponentInstalledEvent, which ServiceComponentHostImpl publishes 
from
    its constructor while the host component row is first persisted with desired
    state INIT. TelemetryAssignmentCompiler skips any component that is not in 
an
    installed state - and INIT is not one of them - so the assignment compiled 
at
    that moment never contains component targets.
    
    Nothing recompiles it on the normal install/start path either: 
handleEvent(),
    which processes the real state transitions, refreshes HostLevelParamsHolder 
but
    never TelemetryHolder. A freshly installed cluster therefore gets no VMAGENT
    component scrape targets until an unrelated cluster config change or an
    ambari-server restart forces a recompile.
    
    Refresh TelemetryHolder from handleEvent(), in the same block that already
    publishes HostComponentsUpdateEvent on a real status change. TelemetryHolder
    .getCurrentData(Long) is widened from protected to public so it is callable
    from there, matching how HostLevelParamsHolder already exposes it.
    
    Adds two tests:
    - TelemetryAssignmentCompilerTest asserts a component in INIT contributes no
      targets while INSTALLED and STARTED each contribute one, documenting why
      subscribing to ServiceComponentInstalledEvent cannot work.
    - ServiceComponentHostTest asserts a real state transition repopulates the
      host's telemetry assignment. The cached assignment is dropped after
      createEvent(), because creating a config publishes 
ClusterConfigChangedEvent,
      which TelemetryHolder also refreshes on - the same mechanism that masks 
this
      bug on a real cluster.
---
 .../ambari/server/agent/stomp/TelemetryHolder.java |  2 +-
 .../svccomphost/ServiceComponentHostImpl.java      | 11 ++++
 .../stomp/TelemetryAssignmentCompilerTest.java     | 59 ++++++++++++++++++++++
 .../svccomphost/ServiceComponentHostTest.java      | 35 +++++++++++++
 4 files changed, 106 insertions(+), 1 deletion(-)

diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TelemetryHolder.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TelemetryHolder.java
index 241aa77419..390308d734 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TelemetryHolder.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/agent/stomp/TelemetryHolder.java
@@ -58,7 +58,7 @@ public class TelemetryHolder extends 
AgentHostDataHolder<TelemetryUpdateEvent> {
   }
 
   @Override
-  protected TelemetryUpdateEvent getCurrentData(Long hostId) throws 
AmbariException {
+  public TelemetryUpdateEvent getCurrentData(Long hostId) throws 
AmbariException {
     return compiler.compile(hostId);
   }
 
diff --git 
a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
 
b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
index 6e079c3f71..988fe752aa 100644
--- 
a/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
+++ 
b/ambari-server/src/main/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostImpl.java
@@ -32,6 +32,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
 import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.agent.AlertDefinitionCommand;
 import org.apache.ambari.server.agent.stomp.HostLevelParamsHolder;
+import org.apache.ambari.server.agent.stomp.TelemetryHolder;
 import org.apache.ambari.server.agent.stomp.TopologyHolder;
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.controller.AmbariManagementController;
@@ -145,6 +146,9 @@ public class ServiceComponentHostImpl implements 
ServiceComponentHost {
   @Inject
   private Provider<HostLevelParamsHolder> m_hostLevelParamsHolder;
 
+  @Inject
+  private Provider<TelemetryHolder> m_telemetryHolder;
+
   /**
    * Used for creating commands to send to the agents when alert definitions 
are
    * added as the result of a service install.
@@ -1024,6 +1028,13 @@ public class ServiceComponentHostImpl implements 
ServiceComponentHost {
         if (statusUpdated) {
           STOMPUpdatePublisher.publish(new 
HostComponentsUpdateEvent(Collections.singletonList(
               HostComponentUpdate.createHostComponentStatusUpdate(stateEntity, 
oldState))));
+          // TelemetryHolder only subscribes to 
ServiceComponentInstalledEvent, which fires
+          // from the host component's constructor with desired state INIT - a 
state
+          // TelemetryAssignmentCompiler excludes - so the first compiled 
assignment for a
+          // host never contains component targets. Refresh here instead, on 
every real
+          // state transition, so scrape targets appear without an unrelated 
config change.
+          m_telemetryHolder.get().updateData(
+              m_telemetryHolder.get().getCurrentData(getHost().getHostId()));
         }
         if 
(event.getType().equals(ServiceComponentHostEventType.HOST_SVCCOMP_STARTED)) {
           HostComponentDesiredStateEntity desiredStateEntity = 
getDesiredStateEntity();
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TelemetryAssignmentCompilerTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TelemetryAssignmentCompilerTest.java
index 105e6cbbac..8fb1f7e225 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TelemetryAssignmentCompilerTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/agent/stomp/TelemetryAssignmentCompilerTest.java
@@ -109,6 +109,65 @@ public class TelemetryAssignmentCompilerTest {
     assertTrue(event.getProfiles().containsKey(EXPECTED_PROFILE_HASH));
   }
 
+  /**
+   * A component only contributes a scrape target once it has left {@link 
State#INIT}.
+   * This is what makes subscribing to {@code ServiceComponentInstalledEvent} 
useless for
+   * telemetry: {@code ServiceComponentHostImpl} publishes that event while 
persisting the
+   * host component row with desired state INIT, long before the component is 
installed or
+   * started, so an assignment compiled at that moment carries no component 
targets at all.
+   * The refresh therefore has to happen on real state transitions instead.
+   */
+  @Test
+  public void testTargetsAppearOnlyAfterComponentLeavesInitState() throws 
Exception {
+    assertEquals(0, compileTargetCount(State.INIT));
+    assertEquals(1, compileTargetCount(State.INSTALLED));
+    assertEquals(1, compileTargetCount(State.STARTED));
+  }
+
+  private int compileTargetCount(State componentState) throws Exception {
+    File serviceDirectory = temporaryFolder.newFolder("HDFS-" + 
componentState);
+    File profileDirectory = new File(serviceDirectory, "telemetry-profiles");
+    assertTrue(profileDirectory.mkdir());
+    File profileFile = new File(profileDirectory, "test-profile.json");
+    Files.write(profileFile.toPath(), 
profileJson().getBytes(StandardCharsets.UTF_8));
+    File descriptorFile = new File(serviceDirectory, "telemetry.json");
+    Files.write(descriptorFile.toPath(), 
descriptorJson().getBytes(StandardCharsets.UTF_8));
+
+    Clusters clusters = createMock(Clusters.class);
+    Host host = createMock(Host.class);
+    Cluster cluster = createMock(Cluster.class);
+    ServiceComponentHost componentHost = 
createMock(ServiceComponentHost.class);
+    AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
+    ConfigHelper configHelper = createMock(ConfigHelper.class);
+    ServiceInfo serviceInfo = new ServiceInfo();
+    serviceInfo.setTelemetryFile(descriptorFile);
+
+    expect(clusters.getHostById(7L)).andReturn(host);
+    expect(host.getHostName()).andStubReturn("nn1.example.com");
+    expect(clusters.getClustersForHost("nn1.example.com"))
+        .andReturn(Collections.singleton(cluster));
+    expect(cluster.getClusterId()).andStubReturn(11L);
+    expect(cluster.getClusterName()).andStubReturn("cluster-one");
+    expect(cluster.getDesiredStackVersion()).andReturn(new StackId("BIGTOP", 
"3.2.0"));
+    expect(cluster.getSecurityType()).andStubReturn(SecurityType.NONE);
+    expect(cluster.getServiceComponentHosts("nn1.example.com"))
+        .andReturn(Collections.singletonList(componentHost));
+    expect(configHelper.getEffectiveConfigProperties("cluster-one", 
"nn1.example.com"))
+        .andReturn(configurations());
+    expect(componentHost.getState()).andReturn(componentState);
+    expect(componentHost.getServiceName()).andStubReturn("HDFS");
+    expect(componentHost.getServiceComponentName()).andStubReturn("NAMENODE");
+    expect(componentHost.getHostName()).andStubReturn("nn1.example.com");
+    expect(metaInfo.getService("BIGTOP", "3.2.0", 
"HDFS")).andStubReturn(serviceInfo);
+    replay(clusters, host, cluster, componentHost, metaInfo, configHelper);
+
+    Provider<Clusters> clustersProvider = () -> clusters;
+    TelemetryAssignmentCompiler compiler =
+        new TelemetryAssignmentCompiler(clustersProvider, metaInfo, 
configHelper);
+
+    return compiler.compile(7L).getAssignment().path("targets").size();
+  }
+
   private Map<String, Map<String, String>> configurations() {
     Map<String, Map<String, String>> configurations = new HashMap<>();
     Map<String, String> hdfsSite = new HashMap<>();
diff --git 
a/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
 
b/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
index bfe96b2952..8354ba3504 100644
--- 
a/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
+++ 
b/ambari-server/src/test/java/org/apache/ambari/server/state/svccomphost/ServiceComponentHostTest.java
@@ -32,6 +32,7 @@ import org.apache.ambari.server.AmbariException;
 import org.apache.ambari.server.H2DatabaseCleaner;
 import org.apache.ambari.server.ServiceComponentNotFoundException;
 import org.apache.ambari.server.ServiceNotFoundException;
+import org.apache.ambari.server.agent.stomp.TelemetryHolder;
 import org.apache.ambari.server.controller.ServiceComponentHostResponse;
 import org.apache.ambari.server.orm.GuiceJpaInitializer;
 import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
@@ -542,6 +543,40 @@ public class ServiceComponentHostTest {
     Assert.assertEquals(State.INSTALLED, sch.getDesiredState());
   }
 
+  /**
+   * TelemetryHolder's only per-component subscription is 
ServiceComponentInstalledEvent,
+   * which is published while the component is still in INIT - a state the 
assignment
+   * compiler excludes - so the assignment it produces never contains 
component targets.
+   * Real state transitions have to refresh it as well, otherwise a freshly 
installed
+   * cluster gets no component scrape targets until an unrelated config change 
or a server
+   * restart forces a recompile.
+   *
+   * <p>The cached assignment is dropped immediately before the transition so 
that what
+   * this asserts is the transition itself repopulating it. It has to be 
dropped after
+   * createEvent(), because creating a config publishes 
ClusterConfigChangedEvent, which
+   * TelemetryHolder also refreshes on - the very thing that masks this bug on 
a real
+   * cluster, where an unrelated config change makes the missing targets 
appear.
+   */
+  @Test
+  public void testTelemetryIsRefreshedOnComponentStateTransition() throws 
Exception {
+    TelemetryHolder telemetryHolder = 
injector.getInstance(TelemetryHolder.class);
+    ServiceComponentHostImpl impl = (ServiceComponentHostImpl)
+        createNewServiceComponentHost(clusterName, "HDFS", "DATANODE", 
hostName1, false);
+    Long hostId = clusters.getHost(hostName1).getHostId();
+    ServiceComponentHostEvent installEvent =
+        createEvent(impl, 1, 
ServiceComponentHostEventType.HOST_SVCCOMP_INSTALL);
+
+    Assert.assertEquals(State.INIT, impl.getState());
+    telemetryHolder.onHostRemoved(hostId);
+    Assert.assertNull(telemetryHolder.getData(hostId));
+
+    impl.handleEvent(installEvent);
+
+    Assert.assertEquals(State.INSTALLING, impl.getState());
+    Assert.assertNotNull("A real state transition must refresh the host's 
telemetry "
+        + "assignment", telemetryHolder.getData(hostId));
+  }
+
   @Test
   @Ignore
   //TODO Should be rewritten after actual configs calculate workflow change.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to