Files
pulse/internal/monitoring/monitored_system_usage.go
T
rcourtman 13bd250328 Reuse the host continuity overlay and copy only hosts for live lookups
While a standalone agent stayed offline across a restart, every canonical
read-state lookup overlaid it from host continuity by building a whole
registry clone. Alert evaluation makes one lookup per resource per poll, so
for up to 72 hours each poll rebuilt the registry once per resource. Each
lookup also called GetLiveHostsSnapshot twice, and that deep-copied every
guest in the monitor state to return the hosts.

The adapter now keeps the last continuity overlay, keyed by registry
pointer, LastRebuiltAt, and a fingerprint of the requested records that
leaves out the build-time UpdatedAt stamp. It reuses the overlay for at
most two seconds, because the overlay evaluates source staleness when it
is built, and it never mutates the live adapter. GetLiveHostsSnapshot now
copies only hosts through State.GetHosts, which also removes a full state
copy from every agent report and config fetch.

With one offline standalone agent, a read-state lookup at 2,080 synthetic
resources went from 136 ms and 95 MB to 74 us and 104 KB.

Refs #2199
2026-09-24 10:57:17 +01:00

201 lines
5.6 KiB
Go

package monitoring
import (
"strings"
"time"
"github.com/rcourtman/pulse-go-rewrite/internal/unifiedresources"
)
// MonitoredSystemUsageSnapshot describes whether the monitor can currently
// supply a canonical monitored-system count that is safe for settings and
// support context to consume.
type MonitoredSystemUsageSnapshot struct {
Count int
ReadState unifiedresources.ReadState
Available bool
UnavailableReason string
}
const (
MonitoredSystemUsageUnavailableMonitorState = "monitor_state_unavailable"
MonitoredSystemUsageUnavailableSupplementalInventoryUnsettled = "supplemental_inventory_unsettled"
MonitoredSystemUsageUnavailableSupplementalInventoryRebuildPending = "supplemental_inventory_rebuild_pending"
)
// MonitoredSystemUsage returns the canonical monitored-system count only when
// the current unified view is settled enough for settings/support context. When
// supplemental provider-owned sources are still settling, the result fails
// closed with Available=false.
func (m *Monitor) MonitoredSystemUsage() MonitoredSystemUsageSnapshot {
if m == nil {
return MonitoredSystemUsageSnapshot{UnavailableReason: MonitoredSystemUsageUnavailableMonitorState}
}
readState := m.GetUnifiedReadStateOrSnapshot()
if readState == nil {
return MonitoredSystemUsageSnapshot{UnavailableReason: MonitoredSystemUsageUnavailableMonitorState}
}
orgID := normalizedMonitorUsageOrgID(m)
readyAt, settled := m.supplementalInventoryReadyAt(orgID)
if !settled {
return MonitoredSystemUsageSnapshot{
ReadState: readState,
UnavailableReason: MonitoredSystemUsageUnavailableSupplementalInventoryUnsettled,
}
}
if !readyAt.IsZero() {
freshness := m.currentUnifiedResourceFreshness()
if freshness.IsZero() || freshness.Before(readyAt) {
return MonitoredSystemUsageSnapshot{
ReadState: readState,
UnavailableReason: MonitoredSystemUsageUnavailableSupplementalInventoryRebuildPending,
}
}
}
readState = m.readStateWithStandaloneHostContinuity(readState)
return MonitoredSystemUsageSnapshot{
Count: unifiedresources.MonitoredSystemCount(readState),
ReadState: readState,
Available: true,
}
}
func (m *Monitor) readStateWithStandaloneHostContinuity(
readState unifiedresources.ReadState,
) unifiedresources.ReadState {
if m == nil || readState == nil {
return readState
}
entries := m.recentStandaloneHostContinuityEntries()
if len(entries) == 0 {
return readState
}
liveHosts := m.GetLiveHostsSnapshot()
liveHostIDs := make(map[string]struct{}, len(liveHosts))
for _, host := range liveHosts {
if id := strings.TrimSpace(host.ID); id != "" {
liveHostIDs[id] = struct{}{}
}
}
existingAgentIDs := make(map[string]struct{}, len(readState.Hosts()))
for _, host := range readState.Hosts() {
if host == nil {
continue
}
if agentID := strings.TrimSpace(host.AgentID()); agentID != "" {
existingAgentIDs[agentID] = struct{}{}
}
if resourceID := strings.TrimSpace(host.ID()); resourceID != "" {
existingAgentIDs[resourceID] = struct{}{}
}
}
records := make([]unifiedresources.IngestRecord, 0, len(entries))
for _, entry := range entries {
hostID := strings.TrimSpace(entry.HostID)
if hostID == "" {
continue
}
if _, ok := liveHostIDs[hostID]; ok {
continue
}
if _, ok := existingAgentIDs[hostID]; ok {
continue
}
// A continuity-only row has no live report in this monitor generation.
// Preserve its last-known identity so the operator can inspect or remove
// it, but never present persisted telemetry as a current online sighting.
host := hostFromContinuityEntry(entry)
host.Status = "offline"
records = append(records, unifiedresources.HostIngestRecord(host))
}
if len(records) == 0 {
return readState
}
return unifiedresources.ReadStateWithHostContinuity(readState, records)
}
func normalizedMonitorUsageOrgID(m *Monitor) string {
if m == nil {
return "default"
}
orgID := strings.TrimSpace(m.GetOrgID())
if orgID == "" {
return "default"
}
return orgID
}
func (m *Monitor) supplementalInventoryReadyAt(orgID string) (time.Time, bool) {
providers := m.supplementalProviderSnapshot()
if len(providers) == 0 {
return time.Time{}, true
}
var watermark time.Time
for _, provider := range providers {
if provider == nil {
continue
}
readinessProvider, hasReadiness := provider.(MonitorSupplementalInventoryReadinessProvider)
if !hasReadiness {
if len(supplementalProviderOwnedSourcesForOrg(provider, orgID)) > 0 {
return time.Time{}, false
}
continue
}
readyAt, settled := readinessProvider.SupplementalInventoryReadyAt(m, orgID)
if !settled {
return time.Time{}, false
}
if readyAt.After(watermark) {
watermark = readyAt
}
}
return watermark, true
}
func supplementalProviderOwnedSourcesForOrg(
provider MonitorSupplementalRecordsProvider,
orgID string,
) []unifiedresources.DataSource {
if provider == nil {
return nil
}
var sources []unifiedresources.DataSource
if tenantOwner, ok := provider.(interface {
SnapshotOwnedSourcesForOrg(string) []unifiedresources.DataSource
}); ok {
sources = tenantOwner.SnapshotOwnedSourcesForOrg(orgID)
} else if owner, ok := provider.(interface {
SnapshotOwnedSources() []unifiedresources.DataSource
}); ok {
sources = owner.SnapshotOwnedSources()
}
if len(sources) == 0 {
return nil
}
out := make([]unifiedresources.DataSource, 0, len(sources))
for _, source := range sources {
normalized := unifiedresources.DataSource(strings.ToLower(strings.TrimSpace(string(source))))
if normalized == "" {
continue
}
out = append(out, normalized)
}
return out
}