From df249b24f6dbac824625df8d67a0b59e912f8cb6 Mon Sep 17 00:00:00 2001 From: henrygd Date: Sun, 26 Apr 2026 19:24:12 -0400 Subject: [PATCH] updates --- agent/probe.go | 13 +++++---- agent/probe_test.go | 31 +++++++++++---------- internal/entities/probe/probe.go | 4 ++- internal/hub/probes.go | 3 +- internal/hub/systems/system.go | 8 ++++-- internal/site/src/lib/use-network-probes.ts | 2 +- internal/site/src/types.d.ts | 5 +++- 7 files changed, 40 insertions(+), 26 deletions(-) diff --git a/agent/probe.go b/agent/probe.go index fb0fc3af..3518cd12 100644 --- a/agent/probe.go +++ b/agent/probe.go @@ -393,6 +393,7 @@ func (task *probeTask) aggregateLocked(duration time.Duration, now time.Time) pr return aggregateBucketsSince(task.buckets[:], cutoff, now) } +// resultLocked returns the aggregated probe result for the requested duration along with a bool indicating whether any data was available. func (task *probeTask) resultLocked(duration time.Duration, now time.Time) (probe.Result, bool) { agg := task.aggregateLocked(duration, now) hourAgg := task.aggregateLocked(time.Hour, now) @@ -401,18 +402,20 @@ func (task *probeTask) resultLocked(duration time.Duration, now time.Time) (prob } result := agg.result() - hourAvg := hourAgg.avgResponse() - hourLoss := hourAgg.lossPercentage() + loss1m := result[3] + response1h := hourAgg.avgResponse() + loss1h := hourAgg.lossPercentage() if hourAgg.successCount > 0 { return probe.Result{ result[0], - hourAvg, + response1h, float64(hourAgg.minUs), float64(hourAgg.maxUs), - hourLoss, + loss1m, + loss1h, }, true } - return probe.Result{result[0], hourAvg, 0, 0, hourLoss}, true + return probe.Result{result[0], response1h, 0, 0, loss1m, loss1h}, true } // aggregateSamplesSince aggregates raw samples newer than the cutoff. diff --git a/agent/probe_test.go b/agent/probe_test.go index c788f19b..0aface32 100644 --- a/agent/probe_test.go +++ b/agent/probe_test.go @@ -22,8 +22,8 @@ func TestProbeTaskAggregateLockedUsesRawSamplesForShortWindows(t *testing.T) { agg := task.aggregateLocked(time.Minute, now) require.True(t, agg.hasData()) - assert.Equal(t, 2, agg.totalCount) - assert.Equal(t, 1, agg.successCount) + assert.Equal(t, int64(2), agg.totalCount) + assert.Equal(t, int64(1), agg.successCount) assert.Equal(t, 20.0, agg.result()[0]) assert.Equal(t, 20.0, agg.result()[1]) assert.Equal(t, 20.0, agg.result()[2]) @@ -42,8 +42,8 @@ func TestProbeTaskAggregateLockedUsesMinuteBucketsForLongWindows(t *testing.T) { agg := task.aggregateLocked(10*time.Minute, now) require.True(t, agg.hasData()) - assert.Equal(t, 4, agg.totalCount) - assert.Equal(t, 3, agg.successCount) + assert.Equal(t, int64(4), agg.totalCount) + assert.Equal(t, int64(3), agg.successCount) assert.Equal(t, 30.0, agg.result()[0]) assert.Equal(t, 20.0, agg.result()[1]) assert.Equal(t, 40.0, agg.result()[2]) @@ -62,8 +62,8 @@ func TestProbeTaskAddSampleLockedTrimsRawSamplesButKeepsBucketHistory(t *testing agg := task.aggregateLocked(10*time.Minute, now) require.True(t, agg.hasData()) - assert.Equal(t, 2, agg.totalCount) - assert.Equal(t, 2, agg.successCount) + assert.Equal(t, int64(2), agg.totalCount) + assert.Equal(t, int64(2), agg.successCount) assert.Equal(t, 15.0, agg.result()[0]) assert.Equal(t, 10.0, agg.result()[1]) assert.Equal(t, 20.0, agg.result()[2]) @@ -76,20 +76,21 @@ func TestProbeManagerGetResultsIncludesHourResponseRange(t *testing.T) { task.addSampleLocked(probeSample{responseUs: 10, timestamp: now.Add(-30 * time.Minute)}) task.addSampleLocked(probeSample{responseUs: 20, timestamp: now.Add(-9 * time.Minute)}) task.addSampleLocked(probeSample{responseUs: 40, timestamp: now.Add(-5 * time.Minute)}) - task.addSampleLocked(probeSample{responseUs: -1, timestamp: now.Add(-90 * time.Second)}) - task.addSampleLocked(probeSample{responseUs: 30, timestamp: now.Add(-30 * time.Second)}) + task.addSampleLocked(probeSample{responseUs: 30, timestamp: now.Add(-50 * time.Second)}) + task.addSampleLocked(probeSample{responseUs: -1, timestamp: now.Add(-30 * time.Second)}) pm := &ProbeManager{probes: map[string]*probeTask{"icmp:example.com": task}} results := pm.GetResults(uint16(time.Minute / time.Millisecond)) result, ok := results["probe-1"] require.True(t, ok) - require.Len(t, result, 5) + require.Len(t, result, 6) assert.Equal(t, 30.0, result[0]) assert.Equal(t, 25.0, result[1]) assert.Equal(t, 10.0, result[2]) assert.Equal(t, 40.0, result[3]) - assert.Equal(t, 20.0, result[4]) + assert.Equal(t, 50.0, result[4]) + assert.Equal(t, 20.0, result[5]) } func TestProbeManagerGetResultsIncludesLossOnlyHourData(t *testing.T) { @@ -103,12 +104,13 @@ func TestProbeManagerGetResultsIncludesLossOnlyHourData(t *testing.T) { results := pm.GetResults(uint16(time.Minute / time.Millisecond)) result, ok := results["probe-1"] require.True(t, ok) - require.Len(t, result, 5) + require.Len(t, result, 6) assert.Equal(t, 0.0, result[0]) assert.Equal(t, 0.0, result[1]) assert.Equal(t, 0.0, result[2]) assert.Equal(t, 0.0, result[3]) assert.Equal(t, 100.0, result[4]) + assert.Equal(t, 100.0, result[5]) } func TestProbeConfigResultKeyUsesSyncedID(t *testing.T) { @@ -205,9 +207,10 @@ func TestProbeManagerApplySyncUpsertRunsImmediatelyAndReturnsResult(t *testing.T defer pm.Stop() require.NoError(t, err) - require.Len(t, resp.Result, 5) + require.Len(t, resp.Result, 6) assert.GreaterOrEqual(t, resp.Result[0], 0.0) assert.Equal(t, 0.0, resp.Result[4]) + assert.Equal(t, 0.0, resp.Result[5]) task := pm.probes["probe-1"] require.NotNil(t, task) @@ -247,8 +250,8 @@ func TestProbeManagerUpsertProbeKeepsHistoryWhenOnlyIntervalChanges(t *testing.T agg := updatedTask.aggregateLocked(time.Hour, now) require.True(t, agg.hasData()) - assert.Equal(t, 2, agg.totalCount) - assert.Equal(t, 2, agg.successCount) + assert.Equal(t, int64(2), agg.totalCount) + assert.Equal(t, int64(2), agg.successCount) assert.Equal(t, 18.0, agg.avgResponse()) select { diff --git a/internal/entities/probe/probe.go b/internal/entities/probe/probe.go index 4f0fb9e7..acdd5ea4 100644 --- a/internal/entities/probe/probe.go +++ b/internal/entities/probe/probe.go @@ -44,7 +44,9 @@ type SyncResponse struct { // // 3: max response over the last hour in microseconds // -// 4: packet loss percentage over the last hour (0-100) +// 4: packet loss percentage (0-100) +// +// 5: packet loss percentage over the last hour (0-100) type Result []float64 // Get returns the value at the specified index or 0 if the index is out of range. diff --git a/internal/hub/probes.go b/internal/hub/probes.go index bb136e3e..b42db2da 100644 --- a/internal/hub/probes.go +++ b/internal/hub/probes.go @@ -117,7 +117,8 @@ func setProbeResultFields(record *core.Record, result probe.Result) { record.Set("resAvg1h", result.Get(1)) record.Set("resMin1h", result.Get(2)) record.Set("resMax1h", result.Get(3)) - record.Set("loss1h", result.Get(4)) + record.Set("loss", result.Get(4)) + record.Set("loss1h", result.Get(5)) record.Set("updated", nowString) } diff --git a/internal/hub/systems/system.go b/internal/hub/systems/system.go index d378addb..de877f92 100644 --- a/internal/hub/systems/system.go +++ b/internal/hub/systems/system.go @@ -334,7 +334,7 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst var updateQuery *dbx.Query if !realtimeActive { db = app.DB() - sql := fmt.Sprintf("UPDATE %s SET res={:res}, resMin1h={:resMin1h}, resMax1h={:resMax1h}, resAvg1h={:resAvg1h}, loss1h={:loss1h}, updated={:updated} WHERE id={:id}", collectionName) + sql := fmt.Sprintf("UPDATE %s SET res={:res}, resMin1h={:resMin1h}, resMax1h={:resMax1h}, resAvg1h={:resAvg1h}, loss={:loss}, loss1h={:loss1h}, updated={:updated} WHERE id={:id}", collectionName) updateQuery = db.NewQuery(sql) } @@ -349,7 +349,8 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst record.Set("resAvg1h", values.Get(1)) record.Set("resMin1h", values.Get(2)) record.Set("resMax1h", values.Get(3)) - record.Set("loss1h", values.Get(4)) + record.Set("loss", values.Get(4)) + record.Set("loss1h", values.Get(5)) record.Set("updated", nowString) err = app.SaveNoValidate(record) } @@ -360,7 +361,8 @@ func updateNetworkProbesRecords(app core.App, data map[string]probe.Result, syst "resAvg1h": values.Get(1), "resMin1h": values.Get(2), "resMax1h": values.Get(3), - "loss1h": values.Get(4), + "loss": values.Get(4), + "loss1h": values.Get(5), "updated": nowString, }).Execute() } diff --git a/internal/site/src/lib/use-network-probes.ts b/internal/site/src/lib/use-network-probes.ts index 4082c3b9..a9b66aa5 100644 --- a/internal/site/src/lib/use-network-probes.ts +++ b/internal/site/src/lib/use-network-probes.ts @@ -248,7 +248,7 @@ export function useNetworkProbesData(props: UseNetworkProbesProps) { // const stats: NetworkProbeStatsRecord["stats"] = {} // for (const probe of probes) { // // TODO: include only if probe.updated < charttime -// stats[probe.id] = [probe.res, probe.resAvg1h, probe.resMin1h, probe.resMax1h, probe.loss1h] +// stats[probe.id] = [probe.res, probe.resAvg1h, probe.resMin1h, probe.resMax1h, probe.loss, probe.loss1h] // } // return stats // } diff --git a/internal/site/src/types.d.ts b/internal/site/src/types.d.ts index 2d83226e..945b0d52 100644 --- a/internal/site/src/types.d.ts +++ b/internal/site/src/types.d.ts @@ -556,6 +556,7 @@ export interface NetworkProbeRecord { resMin1h: number resMax1h: number resAvg1h: number + loss: number loss1h: number interval: number enabled: boolean @@ -571,7 +572,9 @@ export interface NetworkProbeRecord { * * 3: max response over the last hour in microseconds * - * 4: packet loss over 1 hour in % + * 4: packet loss % + * + * 5: packet loss over the last hour in % */ type ProbeResult = number[]