Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
db6f666ff7 |
@@ -626,11 +626,16 @@ func probeStatIface(agentID int64) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// probeReport ingests one fast report (V2.0.1): a probe metrics
|
// probeReport ingests one fast report (V2.0.1): a probe metrics
|
||||||
// block and nothing else. It deliberately does NOT touch the
|
// block and nothing else. Since V2.0.3 it also touches the agent's
|
||||||
// agent's last-seen stamp — liveness stays the 60 s heartbeat's
|
// last-seen stamp, like every other report channel: the hub's
|
||||||
// signal, and the traffic ledger's unseen-boot rule keeps measuring
|
// themes gate each batch on the node's last_seen being newer than
|
||||||
// reboots against heartbeat sightings exactly as before. Agents on
|
// the last one they applied, so a metrics-only channel that never
|
||||||
// older masters never see this endpoint (404) and simply keep
|
// advanced the stamp froze the public cards between the 10 s
|
||||||
|
// traffic reports. Liveness now means "any report from the agent"
|
||||||
|
// — all channels come from the same process — and the traffic
|
||||||
|
// ledger's unseen-boot rule is unaffected: Record still receives
|
||||||
|
// the pre-touch stamp, so gaps measure exactly as before. Agents
|
||||||
|
// on older masters never see this endpoint (404) and simply keep
|
||||||
// reporting metrics on the heartbeat; masters accept metrics on
|
// reporting metrics on the heartbeat; masters accept metrics on
|
||||||
// either channel.
|
// either channel.
|
||||||
func (a *AgentController) probeReport(c *gin.Context) {
|
func (a *AgentController) probeReport(c *gin.Context) {
|
||||||
@@ -644,9 +649,13 @@ func (a *AgentController) probeReport(c *gin.Context) {
|
|||||||
ProbeMetrics *heartbeatProbeMetrics `json:"probeMetrics"`
|
ProbeMetrics *heartbeatProbeMetrics `json:"probeMetrics"`
|
||||||
}
|
}
|
||||||
_ = c.ShouldBindJSON(&req)
|
_ = c.ShouldBindJSON(&req)
|
||||||
|
// The last-seen stamp as it stood before this report (see the
|
||||||
|
// heartbeat handler): captured before touchAgent overwrites it.
|
||||||
|
prevLastSeen := agent.LastSeen
|
||||||
|
a.touchAgent(uint(agent.Id), "")
|
||||||
if req.ProbeMetrics != nil {
|
if req.ProbeMetrics != nil {
|
||||||
a.updateAgentProbe(uint(agent.Id), nil, req.ProbeMetrics)
|
a.updateAgentProbe(uint(agent.Id), nil, req.ProbeMetrics)
|
||||||
probe.Record(int64(agent.Id), probeSample(req.ProbeMetrics), agent.LastSeen)
|
probe.Record(int64(agent.Id), probeSample(req.ProbeMetrics), prevLastSeen)
|
||||||
}
|
}
|
||||||
jsonObj(c, gin.H{"message": "ok"}, nil)
|
jsonObj(c, gin.H{"message": "ok"}, nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// V2.0.1 probe batch, master side: the fast-report endpoint ingests
|
// V2.0.1 probe batch, master side: the fast-report endpoint ingests
|
||||||
// metrics without touching liveness (last-seen stays the
|
// metrics, and the config poll carries the cadence + the node's
|
||||||
// heartbeat's), and the config poll carries the cadence + the
|
// counted-interface pin. Since V2.0.3 the fast report also stamps
|
||||||
// node's counted-interface pin.
|
// last-seen (the hub's themes gate batches on its freshness); it
|
||||||
|
// originally did not — see the probeReport handler comment.
|
||||||
func TestAgentFastReportAndConfigKeys(t *testing.T) {
|
func TestAgentFastReportAndConfigKeys(t *testing.T) {
|
||||||
gin.SetMode(gin.TestMode)
|
gin.SetMode(gin.TestMode)
|
||||||
dbDir := t.TempDir()
|
dbDir := t.TempDir()
|
||||||
@@ -71,6 +72,12 @@ func TestAgentFastReportAndConfigKeys(t *testing.T) {
|
|||||||
|
|
||||||
metrics := `{"probeMetrics":{"bootId":"boot-f/0123456789abcdef","uptime":60,` +
|
metrics := `{"probeMetrics":{"bootId":"boot-f/0123456789abcdef","uptime":60,` +
|
||||||
`"memTotal":1000,"memUsed":400,"netRxTotal":10,"netTxTotal":20}}`
|
`"memTotal":1000,"memUsed":400,"netRxTotal":10,"netTxTotal":20}}`
|
||||||
|
// Age the stamp first so the fast report's touch is observable
|
||||||
|
// even within the same millisecond as the heartbeat's.
|
||||||
|
stale := seenAfterHeartbeat - 120_000
|
||||||
|
if err := db.Model(&model.AgentNode{}).Where("id = ?", agent.Id).Update("last_seen", stale).Error; err != nil {
|
||||||
|
t.Fatalf("age last-seen: %v", err)
|
||||||
|
}
|
||||||
if code, body := call(http.MethodPost, "/panel/api/agent/probe-report", metrics); code != http.StatusOK {
|
if code, body := call(http.MethodPost, "/panel/api/agent/probe-report", metrics); code != http.StatusOK {
|
||||||
t.Fatalf("probe-report: %d (%s)", code, body)
|
t.Fatalf("probe-report: %d (%s)", code, body)
|
||||||
}
|
}
|
||||||
@@ -78,8 +85,8 @@ func TestAgentFastReportAndConfigKeys(t *testing.T) {
|
|||||||
if row.ProbeMemUsed != 400 || row.ProbeAt == 0 {
|
if row.ProbeMemUsed != 400 || row.ProbeAt == 0 {
|
||||||
t.Errorf("fast report metrics not stored: %+v", row)
|
t.Errorf("fast report metrics not stored: %+v", row)
|
||||||
}
|
}
|
||||||
if row.LastSeen != seenAfterHeartbeat {
|
if row.LastSeen <= stale {
|
||||||
t.Errorf("fast report moved last-seen: %d -> %d (liveness is the heartbeat's signal)", seenAfterHeartbeat, row.LastSeen)
|
t.Errorf("fast report did not stamp last-seen: %d -> %d (V2.0.3: themes gate on it)", stale, row.LastSeen)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unauthorized callers are refused.
|
// Unauthorized callers are refused.
|
||||||
|
|||||||
@@ -29,9 +29,11 @@ import (
|
|||||||
// 150 s absorbs one lost beat plus jitter.
|
// 150 s absorbs one lost beat plus jitter.
|
||||||
const ProbeOnlineThresholdMs = 150_000
|
const ProbeOnlineThresholdMs = 150_000
|
||||||
|
|
||||||
// ProbeSnapshotTTL mirrors the hub's 1900 ms frame reuse: just under
|
// ProbeSnapshotTTL mirrors the hub's frame reuse, shortened in
|
||||||
// the 2 s push interval, so a frame is built at most once per tick.
|
// V2.0.3 from the hub's 1900 ms: just under the fastest (1 s) push
|
||||||
const ProbeSnapshotTTL = 1900 * time.Millisecond
|
// cadence, so a 1 s stream still gets a fresh frame every tick
|
||||||
|
// instead of repeating the previous one half the time.
|
||||||
|
const ProbeSnapshotTTL = 900 * time.Millisecond
|
||||||
|
|
||||||
// ---- public-surface settings (main settings KV, hub key names) ----
|
// ---- public-surface settings (main settings KV, hub key names) ----
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user