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]