fix: stamp environment on agent_events rows
The agent_events table has an `environment` column and AgentEventsController
filters on it, but the INSERT never populated it — every row got the
column default ('default'). Result: Timeline on the Application Runtime
page was empty whenever the user's selected env was anything other than
'default'.
Thread env through the write path:
- AgentEventRepository.insert + AgentEventService.recordEvent gain an
`environment` param; delete the no-env query overload (unused).
- ClickHouseAgentEventRepository.insert writes the column (falls back to
'default' on null to match column DEFAULT).
- All 5 callers source env from the agent registry (AgentInfo.environmentId)
or the registration request body; AgentLifecycleMonitor, deregister,
command ack, event ingestion, register/re-register.
- Integration test updated for the new signatures.
Pre-existing rows in deployed CH will still report environment='default'.
New events from this build forward will carry the correct env. Backfill
(UPDATE ... FROM apps) is left as a manual DB step if historical timeline
is needed for non-default envs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -55,7 +55,8 @@ public class AgentLifecycleMonitor {
|
||||
if (before != null && before != agent.state()) {
|
||||
String eventType = mapTransitionEvent(before, agent.state());
|
||||
if (eventType != null) {
|
||||
agentEventService.recordEvent(agent.instanceId(), agent.applicationId(), eventType,
|
||||
agentEventService.recordEvent(agent.instanceId(), agent.applicationId(),
|
||||
agent.environmentId(), eventType,
|
||||
agent.displayName() + " " + before + " -> " + agent.state());
|
||||
serverMetrics.recordAgentTransition(eventType);
|
||||
}
|
||||
|
||||
@@ -223,7 +223,8 @@ public class AgentCommandController {
|
||||
if (body != null && body.status() != null) {
|
||||
AgentInfo agent = registryService.findById(id);
|
||||
String application = agent != null ? agent.applicationId() : "unknown";
|
||||
agentEventService.recordEvent(id, application, "COMMAND_" + body.status(),
|
||||
String environment = agent != null ? agent.environmentId() : null;
|
||||
agentEventService.recordEvent(id, application, environment, "COMMAND_" + body.status(),
|
||||
"Command " + commandId + ": " + body.message());
|
||||
log.debug("Command {} ack from agent {}: {} - {}", commandId, id, body.status(), body.message());
|
||||
}
|
||||
|
||||
@@ -143,12 +143,12 @@ public class AgentRegistrationController {
|
||||
if (reRegistration) {
|
||||
log.info("Agent re-registered: {} (application={}, routes={}, capabilities={})",
|
||||
request.instanceId(), application, routeIds.size(), capabilities.keySet());
|
||||
agentEventService.recordEvent(request.instanceId(), application, "RE_REGISTERED",
|
||||
agentEventService.recordEvent(request.instanceId(), application, environmentId, "RE_REGISTERED",
|
||||
"Agent re-registered with " + routeIds.size() + " routes");
|
||||
} else {
|
||||
log.info("Agent registered: {} (application={}, routes={})",
|
||||
request.instanceId(), application, routeIds.size());
|
||||
agentEventService.recordEvent(request.instanceId(), application, "REGISTERED",
|
||||
agentEventService.recordEvent(request.instanceId(), application, environmentId, "REGISTERED",
|
||||
"Agent registered: " + request.instanceId());
|
||||
}
|
||||
|
||||
@@ -315,8 +315,9 @@ public class AgentRegistrationController {
|
||||
return ResponseEntity.notFound().build();
|
||||
}
|
||||
String applicationId = agent.applicationId();
|
||||
String environment = agent.environmentId();
|
||||
registryService.deregister(id);
|
||||
agentEventService.recordEvent(id, applicationId, "DEREGISTERED", "Agent deregistered");
|
||||
agentEventService.recordEvent(id, applicationId, environment, "DEREGISTERED", "Agent deregistered");
|
||||
auditService.log(id, "agent_deregister", AuditCategory.AGENT, id, null, AuditResult.SUCCESS, httpRequest);
|
||||
return ResponseEntity.ok().build();
|
||||
}
|
||||
|
||||
@@ -71,9 +71,10 @@ public class EventIngestionController {
|
||||
|
||||
AgentInfo agent = registryService.findById(instanceId);
|
||||
String applicationId = agent != null ? agent.applicationId() : "";
|
||||
String environment = agent != null ? agent.environmentId() : null;
|
||||
|
||||
for (AgentEvent event : events) {
|
||||
agentEventService.recordEvent(instanceId, applicationId,
|
||||
agentEventService.recordEvent(instanceId, applicationId, environment,
|
||||
event.getEventType(),
|
||||
event.getDetails() != null ? event.getDetails().toString() : null);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.List;
|
||||
public class ClickHouseAgentEventRepository implements AgentEventRepository {
|
||||
|
||||
private static final String INSERT_SQL =
|
||||
"INSERT INTO agent_events (tenant_id, instance_id, application_id, event_type, detail) VALUES (?, ?, ?, ?, ?)";
|
||||
"INSERT INTO agent_events (tenant_id, instance_id, application_id, environment, event_type, detail) VALUES (?, ?, ?, ?, ?, ?)";
|
||||
|
||||
private static final String SELECT_BASE =
|
||||
"SELECT 0 AS id, instance_id, application_id, event_type, detail, timestamp FROM agent_events WHERE tenant_id = ?";
|
||||
@@ -32,13 +32,9 @@ public class ClickHouseAgentEventRepository implements AgentEventRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(String instanceId, String applicationId, String eventType, String detail) {
|
||||
jdbc.update(INSERT_SQL, tenantId, instanceId, applicationId, eventType, detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AgentEventRecord> query(String applicationId, String instanceId, Instant from, Instant to, int limit) {
|
||||
return query(applicationId, instanceId, null, from, to, limit);
|
||||
public void insert(String instanceId, String applicationId, String environment, String eventType, String detail) {
|
||||
jdbc.update(INSERT_SQL, tenantId, instanceId, applicationId,
|
||||
environment != null ? environment : "default", eventType, detail);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -49,15 +49,15 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
*/
|
||||
private void insertAt(String instanceId, String applicationId, String eventType, String detail, Instant ts) {
|
||||
jdbc.update(
|
||||
"INSERT INTO agent_events (tenant_id, instance_id, application_id, event_type, detail, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
|
||||
"default", instanceId, applicationId, eventType, detail, Timestamp.from(ts));
|
||||
"INSERT INTO agent_events (tenant_id, instance_id, application_id, environment, event_type, detail, timestamp) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
||||
"default", instanceId, applicationId, "default", eventType, detail, Timestamp.from(ts));
|
||||
}
|
||||
|
||||
// ── Tests ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@Test
|
||||
void insert_writesEvent() {
|
||||
repo.insert("agent-1", "app-a", "CONNECTED", "agent came online");
|
||||
repo.insert("agent-1", "app-a", "default", "CONNECTED", "agent came online");
|
||||
|
||||
Long count = jdbc.queryForObject(
|
||||
"SELECT count() FROM agent_events WHERE instance_id = 'agent-1'",
|
||||
@@ -67,10 +67,10 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
|
||||
@Test
|
||||
void query_byAppId_filtersCorrectly() {
|
||||
repo.insert("agent-1", "app-x", "CONNECTED", "");
|
||||
repo.insert("agent-2", "app-y", "DISCONNECTED", "");
|
||||
repo.insert("agent-1", "app-x", "default", "CONNECTED", "");
|
||||
repo.insert("agent-2", "app-y", "default", "DISCONNECTED", "");
|
||||
|
||||
List<AgentEventRecord> results = repo.query("app-x", null, null, null, 100);
|
||||
List<AgentEventRecord> results = repo.query("app-x", null, null, null, null, 100);
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).applicationId()).isEqualTo("app-x");
|
||||
@@ -79,10 +79,10 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
|
||||
@Test
|
||||
void query_byAgentId_filtersCorrectly() {
|
||||
repo.insert("agent-alpha", "app-shared", "CONNECTED", "");
|
||||
repo.insert("agent-beta", "app-shared", "CONNECTED", "");
|
||||
repo.insert("agent-alpha", "app-shared", "default", "CONNECTED", "");
|
||||
repo.insert("agent-beta", "app-shared", "default", "CONNECTED", "");
|
||||
|
||||
List<AgentEventRecord> results = repo.query(null, "agent-alpha", null, null, 100);
|
||||
List<AgentEventRecord> results = repo.query(null, "agent-alpha", null, null, null, 100);
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).instanceId()).isEqualTo("agent-alpha");
|
||||
@@ -99,7 +99,7 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
insertAt("agent-1", "app-a", "DISCONNECTED", "late", t3);
|
||||
|
||||
// Query [t2, t3) — should return only the middle event
|
||||
List<AgentEventRecord> results = repo.query(null, null, t2, t3, 100);
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, t2, t3, 100);
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).eventType()).isEqualTo("HEARTBEAT");
|
||||
@@ -112,16 +112,16 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
insertAt("agent-1", "app-a", "HEARTBEAT", "beat-" + i, base.plusSeconds(i));
|
||||
}
|
||||
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, 3);
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, null, 3);
|
||||
|
||||
assertThat(results).hasSize(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void query_returnsZeroId() {
|
||||
repo.insert("agent-1", "app-a", "CONNECTED", "");
|
||||
repo.insert("agent-1", "app-a", "default", "CONNECTED", "");
|
||||
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, 10);
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, null, 10);
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
assertThat(results.get(0).id()).isEqualTo(0L);
|
||||
@@ -129,10 +129,10 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
|
||||
@Test
|
||||
void query_noFilters_returnsAllEvents() {
|
||||
repo.insert("agent-1", "app-a", "CONNECTED", "");
|
||||
repo.insert("agent-2", "app-b", "DISCONNECTED", "");
|
||||
repo.insert("agent-1", "app-a", "default", "CONNECTED", "");
|
||||
repo.insert("agent-2", "app-b", "default", "DISCONNECTED", "");
|
||||
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, 100);
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, null, 100);
|
||||
|
||||
assertThat(results).hasSize(2);
|
||||
}
|
||||
@@ -147,7 +147,7 @@ class ClickHouseAgentEventRepositoryIT {
|
||||
insertAt("agent-1", "app-a", "SECOND", "", t2);
|
||||
insertAt("agent-1", "app-a", "THIRD", "", t3);
|
||||
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, 100);
|
||||
List<AgentEventRecord> results = repo.query(null, null, null, null, null, 100);
|
||||
|
||||
assertThat(results.get(0).eventType()).isEqualTo("THIRD");
|
||||
assertThat(results.get(1).eventType()).isEqualTo("SECOND");
|
||||
|
||||
@@ -5,9 +5,7 @@ import java.util.List;
|
||||
|
||||
public interface AgentEventRepository {
|
||||
|
||||
void insert(String instanceId, String applicationId, String eventType, String detail);
|
||||
|
||||
List<AgentEventRecord> query(String applicationId, String instanceId, Instant from, Instant to, int limit);
|
||||
void insert(String instanceId, String applicationId, String environment, String eventType, String detail);
|
||||
|
||||
List<AgentEventRecord> query(String applicationId, String instanceId, String environment, Instant from, Instant to, int limit);
|
||||
}
|
||||
|
||||
@@ -16,13 +16,9 @@ public class AgentEventService {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public void recordEvent(String instanceId, String applicationId, String eventType, String detail) {
|
||||
log.debug("Recording agent event: instance={}, app={}, type={}", instanceId, applicationId, eventType);
|
||||
repository.insert(instanceId, applicationId, eventType, detail);
|
||||
}
|
||||
|
||||
public List<AgentEventRecord> queryEvents(String applicationId, String instanceId, Instant from, Instant to, int limit) {
|
||||
return repository.query(applicationId, instanceId, from, to, limit);
|
||||
public void recordEvent(String instanceId, String applicationId, String environment, String eventType, String detail) {
|
||||
log.debug("Recording agent event: instance={}, app={}, env={}, type={}", instanceId, applicationId, environment, eventType);
|
||||
repository.insert(instanceId, applicationId, environment, eventType, detail);
|
||||
}
|
||||
|
||||
public List<AgentEventRecord> queryEvents(String applicationId, String instanceId, String environment, Instant from, Instant to, int limit) {
|
||||
|
||||
Reference in New Issue
Block a user