mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-11-11 11:57:57 +00:00
perf: use cached ParsedDecoded() instead of repeated json.Unmarshal (#1871)
## Problem
`StoreTx.ParsedDecoded()` already caches the result of `json.Unmarshal`
on first call via `sync.Once`. However, 13 call sites in `store.go` and
1 in `routes.go` were independently unmarshaling `DecodedJSON` into
local `map[string]interface{}` variables on every access, completely
ignoring the cache.
## Impact
For a store with 50k+ transmissions, each analytics endpoint that
iterates all packets re-parses 50k JSON strings per request. With
multiple endpoints, this means hundreds of thousands of redundant
`json.Unmarshal` calls per page load, each allocating new maps and
slices.
## Fix
Replace each `var d map[string]interface{};
json.Unmarshal([]byte(tx.DecodedJSON), &d)` with `d :=
tx.ParsedDecoded()`, which returns the cached parse result (parsed once,
reused forever).
### Call sites changed (14 total):
- `untrackAdvertPubkey` — advert PK extraction during eviction
- Ingestion path — payload field for API responses
- `evictStaleInternal` — node cleanup during eviction
- `GetAnalyticsTopology` — node PK extraction
- `GetAnalyticsHashCollisions` — advert PK extraction
- `GetAnalyticsDistance` — region node PK building
- `resolveAreaNodes` — node PK extraction
- `GetRecentPackets` — payload field for API response
- `routes.go` byType grouping — type field extraction
### Left unchanged (3 sites):
Three call sites that unmarshal into typed structs (`grpDec`,
`decodedMsg`, `decodedGrp`) cannot use `ParsedDecoded()` since they need
specific struct types.
## Testing
- `go build` passes
- No behavior change — same data, same logic, just avoids redundant
parses
---------
Co-authored-by: Joel Claw <358739783+Joel-Claw@users.noreply.github.com>
This commit is contained in:
1 parent
b38536911a
commit
f49e3fcc26
2 files changed
+29
-24
No files matched your search
@@ -2954,8 +2954,7 @@ func (s *Server) handleAudioLabBuckets(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
typeName := "UNKNOWN"
|
||||
if tx.DecodedJSON != "" {
|
||||
var d map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(tx.DecodedJSON), &d); err == nil {
|
||||
if d := tx.ParsedDecoded(); d != nil {
|
||||
if t, ok := d["type"].(string); ok && t != "" {
|
||||
typeName = t
|
||||
}
|
||||
|
||||
+28
-22
@@ -1798,8 +1798,8 @@ func (s *PacketStore) untrackAdvertPubkey(tx *StoreTx) {
|
||||
if tx.PayloadType == nil || *tx.PayloadType != PayloadADVERT || tx.DecodedJSON == "" {
|
||||
return
|
||||
}
|
||||
var d map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &d) != nil {
|
||||
d := tx.ParsedDecoded()
|
||||
if d == nil {
|
||||
return
|
||||
}
|
||||
pk := ""
|
||||
@@ -2916,9 +2916,14 @@ func (s *PacketStore) IngestNewFromDB(sinceID, limit int) ([]map[string]interfac
|
||||
},
|
||||
}
|
||||
if tx.DecodedJSON != "" {
|
||||
var payload map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &payload) == nil {
|
||||
decoded["payload"] = payload
|
||||
if payload := tx.ParsedDecoded(); payload != nil {
|
||||
// Copy the cached map so broadcast consumers cannot mutate
|
||||
// the shared cache or cause a data race (#1871 review).
|
||||
cp := make(map[string]interface{}, len(payload))
|
||||
for k, v := range payload {
|
||||
cp[k] = v
|
||||
}
|
||||
decoded["payload"] = cp
|
||||
}
|
||||
}
|
||||
// For TRACE packets, decode the full packet to include path.hopsCompleted
|
||||
@@ -3189,9 +3194,14 @@ func (s *PacketStore) IngestNewObservations(sinceObsID, limit int) []map[string]
|
||||
},
|
||||
}
|
||||
if tx.DecodedJSON != "" {
|
||||
var payload map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &payload) == nil {
|
||||
decoded["payload"] = payload
|
||||
if payload := tx.ParsedDecoded(); payload != nil {
|
||||
// Copy the cached map so broadcast consumers cannot mutate
|
||||
// the shared cache or cause a data race (#1871 review).
|
||||
cp := make(map[string]interface{}, len(payload))
|
||||
for k, v := range payload {
|
||||
cp[k] = v
|
||||
}
|
||||
decoded["payload"] = cp
|
||||
}
|
||||
}
|
||||
// For TRACE packets, decode the full packet to include path.hopsCompleted
|
||||
@@ -3737,8 +3747,8 @@ func (s *PacketStore) computeNodeHomeRegions() map[string]string {
|
||||
continue
|
||||
}
|
||||
|
||||
var d map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &d) != nil {
|
||||
d := tx.ParsedDecoded()
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
pk, _ := d["pubKey"].(string)
|
||||
@@ -4651,8 +4661,7 @@ func (s *PacketStore) evictStaleInternal(rpBatch map[int][]string) int {
|
||||
// Must mirror indexByNode: process decoded JSON fields AND resolved_path pubkeys.
|
||||
evictedFromNode := make(map[string]bool)
|
||||
if tx.DecodedJSON != "" {
|
||||
var decoded map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &decoded) == nil {
|
||||
if decoded := tx.ParsedDecoded(); decoded != nil {
|
||||
for _, field := range []string{"pubKey", "destPubKey", "srcPubKey"} {
|
||||
if v, ok := decoded[field].(string); ok && v != "" {
|
||||
if hashes, ok := s.nodeHashes[v]; ok {
|
||||
@@ -5009,8 +5018,7 @@ func computeDistancesForTx(tx *StoreTx, nodeByPk map[string]*nodeInfo, repeaterS
|
||||
|
||||
var senderNode *nodeInfo
|
||||
if tx.DecodedJSON != "" {
|
||||
var dec map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &dec) == nil {
|
||||
if dec := tx.ParsedDecoded(); dec != nil {
|
||||
if pk, ok := dec["pubKey"].(string); ok && pk != "" {
|
||||
senderNode = nodeByPk[pk]
|
||||
}
|
||||
@@ -8030,8 +8038,7 @@ func (s *PacketStore) computeAnalyticsHashSizes(region, area string) map[string]
|
||||
var advertPK, advertName string
|
||||
var advertParsed bool
|
||||
if tx.PayloadType != nil && *tx.PayloadType == PayloadADVERT && tx.DecodedJSON != "" {
|
||||
var d map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &d) == nil {
|
||||
if d := tx.ParsedDecoded(); d != nil {
|
||||
if v, ok := d["pubKey"].(string); ok {
|
||||
advertPK = v
|
||||
} else if v, ok := d["public_key"].(string); ok {
|
||||
@@ -8342,8 +8349,7 @@ func (s *PacketStore) computeHashCollisions(region, area string) map[string]inte
|
||||
}
|
||||
// Collect node public keys from advert packets
|
||||
if tx.DecodedJSON != "" {
|
||||
var d map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &d) == nil {
|
||||
if d := tx.ParsedDecoded(); d != nil {
|
||||
if pk, ok := d["pubKey"].(string); ok && pk != "" {
|
||||
regionNodePKs[pk] = true
|
||||
}
|
||||
@@ -8706,8 +8712,8 @@ func (s *PacketStore) computeNodeHashSizeInfo() map[string]*hashSizeNodeInfo {
|
||||
}
|
||||
hs := int((pathByte>>6)&0x3) + 1
|
||||
|
||||
var d map[string]interface{}
|
||||
if json.Unmarshal([]byte(tx.DecodedJSON), &d) != nil {
|
||||
d := tx.ParsedDecoded()
|
||||
if d == nil {
|
||||
continue
|
||||
}
|
||||
pk := ""
|
||||
@@ -9645,8 +9651,8 @@ func (s *PacketStore) GetNodeAnalytics(pubkey string, days int) (*NodeAnalyticsR
|
||||
if p.DecodedJSON == "" {
|
||||
continue
|
||||
}
|
||||
var decoded map[string]interface{}
|
||||
if json.Unmarshal([]byte(p.DecodedJSON), &decoded) != nil {
|
||||
decoded := p.ParsedDecoded()
|
||||
if decoded == nil {
|
||||
continue
|
||||
}
|
||||
type candidate struct{ key, name string }
|
||||
|
||||
Reference in new issue
Block a user