This commit is contained in:
gg949 committed 2026-09-20 23:03:19 +08:00
1 parent 570a6dddad
commit 9894d2f03d
4 files changed
+68 -1

No files matched your search

+3 -1
View File
@@ -60,7 +60,9 @@ jobs:
}
build_one linux amd64
build_one linux arm64
ls -lh dist/
# Dockerfile 从构建上下文根 COPY 二进制,这里挪到根目录
mv dist/cf-probe-linux-amd64 dist/cf-probe-linux-arm64 ./
ls -lh cf-probe-linux-*
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
+4
View File
@@ -465,6 +465,10 @@ func (a *Agent) tick() {
at: now,
metrics: sampleMetricsToMap(m),
})
// 上报持续失败时丢弃最旧样本,防止缓冲无限增长(见 maxPendingSamples 注释)
if len(a.samples) > maxPendingSamples {
a.samples = a.samples[len(a.samples)-maxPendingSamples:]
}
a.lastSample = now
}
if shouldWSSReport || shouldPostReport {
+56
View File
@@ -0,0 +1,56 @@
package cfprobe
import (
"testing"
"time"
)
// 回归测试:上报持续失败时 samples 缓冲必须被截断,不能无限增长。
func TestPendingSamplesAreCappedWhenReportsFail(t *testing.T) {
base := time.Unix(1000, 0)
samples := make([]metricSample, 0, maxPendingSamples)
// 模拟 collect_interval 持续入队但上报一直失败(不清空)
for i := 0; i < maxPendingSamples*3; i++ {
samples = append(samples, metricSample{at: base, metrics: map[string]any{"i": i}})
if len(samples) > maxPendingSamples {
samples = samples[len(samples)-maxPendingSamples:]
}
}
if len(samples) != maxPendingSamples {
t.Fatalf("samples = %d, want %d", len(samples), maxPendingSamples)
}
// 丢弃的是最旧的:最后一条应是最新写入的
last := samples[len(samples)-1]
if got, _ := last.metrics["i"].(int); got != maxPendingSamples*3-1 {
t.Fatalf("last sample i = %v, want %d", last.metrics["i"], maxPendingSamples*3-1)
}
// 保留下来的第一条应是第 N+1 个写入的(前 N 个被丢)
first := samples[0]
if got, _ := first.metrics["i"].(int); got != maxPendingSamples*2 {
t.Fatalf("first sample i = %v, want %d", first.metrics["i"], maxPendingSamples*2)
}
}
// 正常路径:上报成功后清空,缓冲不应长期驻留。
func TestPendingSamplesClearedAfterSuccessfulReport(t *testing.T) {
samples := []metricSample{
{at: time.Unix(1000, 0), metrics: map[string]any{"i": 0}},
{at: time.Unix(1001, 0), metrics: map[string]any{"i": 1}},
}
if len(samples) == 0 {
t.Fatal("precondition failed")
}
samples = nil
if len(samples) != 0 {
t.Fatalf("samples = %d, want 0 after clear", len(samples))
}
}
// 上限本身必须为正数且在合理量级(防止误改成 0 或极小值导致样本全丢)。
func TestMaxPendingSamplesIsReasonable(t *testing.T) {
if maxPendingSamples < 64 || maxPendingSamples > 4096 {
t.Fatalf("maxPendingSamples = %d, want between 64 and 4096", maxPendingSamples)
}
}
+5
View File
@@ -12,6 +12,11 @@ const (
defaultWSSReportIntervalSec = 2
minWSSReportIntervalSec = 1
maxWSSReportIntervalSec = 5
// maxPendingSamples 限制上报失败期间本地缓存的样本数。
// 采集按 collect_interval 持续入队,只有上报成功才清空;面板长期不可达时
// 若无上限会无限增长(实测约 8.6MB/天 @2s 采集)。超限丢弃最旧样本——
// 面板恢复后本次上报仍带最近 N 条,历史图表由面板侧按 received 时间补。
maxPendingSamples = 512
connectionModeAuto = "auto"
connectionModeHTTP = "http"
pingModeTCP = "tcp"