问题描述
thingspanel-backend-community/internal/storage/telemetry_writer.go 的 fallbackInsert 函数发生 panic:
panic: runtime error: index out of range [8] with length 8
goroutine 43 [running]:
project/internal/storage.(*telemetryWriter).fallbackInsert.func1(...)
...telemetry_writer.go:328
...
根本原因
历史数据表和历史最新值表的去重规则不同:
| 切片 |
去重键 |
同一批次条数 |
historyData(历史表) |
device_id + key + ts |
全部保留(如 3帧×8个指标 = 24条) |
currentData(最新值表) |
device_id + key |
每个指标只留最新(如 8条) |
deduplicateAndConvert(第195-253行)返回的两个切片,当一个 flush 批次内包含同一设备的多个时间戳时,长度天然不一致。
但 fallbackInsert 用同一个下标 i 同时索引两个切片:
for i := range historyData {
...
.Create(&historyData[i]).Error // 正常
.Create(¤tData[i]).Error // 当 i >= len(currentData) 时越界 panic
}
当 len(historyData) > len(currentData) 时,currentData[i] 越界崩溃。
触发条件
- 批量插入失败,降级为逐条插入(进入
fallbackInsert)
- 一个 flush 批次内包含同一设备的多个时间戳(高频设备上报)
我们的场景:传感器每 0.1 秒上报一次,每帧 8 个遥测点。批量插入失败且跨批次时必现崩溃。
影响
- 后端遥测写入 goroutine 崩溃
- 遥测数据可能丢失
- 影响所有设备的遥测入库
修复建议
将历史表和最新值表的插入拆成两个独立循环,各自遍历自己的切片:
// 历史表单独循环
for i := range historyData {
// tx.Create(&historyData[i])
}
// 最新值表单独循环
for i := range currentData {
// tx.Create(¤tData[i])
}
环境
- thingspanel-backend-community
- GORM v1.25.7
- Go 1.22
- TimescaleDB 14
问题描述
thingspanel-backend-community/internal/storage/telemetry_writer.go的fallbackInsert函数发生 panic:根本原因
历史数据表和历史最新值表的去重规则不同:
historyData(历史表)device_id + key + tscurrentData(最新值表)device_id + keydeduplicateAndConvert(第195-253行)返回的两个切片,当一个 flush 批次内包含同一设备的多个时间戳时,长度天然不一致。但
fallbackInsert用同一个下标i同时索引两个切片:当
len(historyData) > len(currentData)时,currentData[i]越界崩溃。触发条件
fallbackInsert)我们的场景:传感器每 0.1 秒上报一次,每帧 8 个遥测点。批量插入失败且跨批次时必现崩溃。
影响
修复建议
将历史表和最新值表的插入拆成两个独立循环,各自遍历自己的切片:
环境