mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-10-23 10:32:23 +00:00
perf: index subpath detail lookups instead of scanning all packets (#571)
## Summary `GetSubpathDetail()` iterated ALL packets to find those containing a specific subpath — `O(packets × hops × subpath_length)`. With 30K+ packets this caused user-visible latency on every subpath detail click. ## Changes ### `cmd/server/store.go` - Added `spTxIndex map[string][]*StoreTx` alongside existing `spIndex` — tracks which transmissions contain each subpath key - Extended `addTxToSubpathIndexFull()` and `removeTxFromSubpathIndexFull()` to maintain both indexes simultaneously - Original `addTxToSubpathIndex()`/`removeTxFromSubpathIndex()` wrappers preserved for backward compatibility - `buildSubpathIndex()` now populates both `spIndex` and `spTxIndex` during `Load()` - All incremental update sites (ingest, path change, eviction) use the `Full` variants - `GetSubpathDetail()` rewritten: direct `O(1)` map lookup on `spTxIndex[key]` instead of scanning all packets ### `cmd/server/coverage_test.go` - Added `TestSubpathTxIndexPopulated`: verifies `spTxIndex` is populated, counts match `spIndex`, and `GetSubpathDetail` returns correct results for both existing and non-existent subpaths ## Complexity - **Before:** `O(total_packets × avg_hops × subpath_length)` per request - **After:** `O(matched_txs)` per request (direct map lookup) ## Tests All tests pass: `cmd/server` (4.6s), `cmd/ingestor` (25.6s) Fixes #358 --------- Co-authored-by: you <you@example.com>
This commit is contained in:
1 parent
37300bf5c8
commit
d4f2c3ac66
2 files changed
+126
-37
No files matched your search
@@ -2192,6 +2192,84 @@ func TestSubpathPrecomputedIndex(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubpathTxIndexPopulated(t *testing.T) {
|
||||
db := setupRichTestDB(t)
|
||||
defer db.Close()
|
||||
store := NewPacketStore(db, nil)
|
||||
store.Load()
|
||||
|
||||
// spTxIndex must be populated alongside spIndex
|
||||
if len(store.spTxIndex) == 0 {
|
||||
t.Fatal("expected spTxIndex to be populated after Load()")
|
||||
}
|
||||
|
||||
// Every key in spIndex must also exist in spTxIndex with matching count
|
||||
for key, count := range store.spIndex {
|
||||
txs, ok := store.spTxIndex[key]
|
||||
if !ok {
|
||||
t.Errorf("spTxIndex missing key %q that exists in spIndex", key)
|
||||
continue
|
||||
}
|
||||
if len(txs) != count {
|
||||
t.Errorf("spTxIndex[%q] has %d txs, spIndex count is %d", key, len(txs), count)
|
||||
}
|
||||
}
|
||||
|
||||
// GetSubpathDetail should return correct match count via indexed lookup
|
||||
detail := store.GetSubpathDetail([]string{"eeff", "0011"})
|
||||
if detail == nil {
|
||||
t.Fatal("expected non-nil detail for existing subpath")
|
||||
}
|
||||
matches, _ := detail["totalMatches"].(int)
|
||||
if matches != 1 {
|
||||
t.Errorf("totalMatches = %d, want 1", matches)
|
||||
}
|
||||
|
||||
// Non-existent subpath should return 0 matches
|
||||
detail2 := store.GetSubpathDetail([]string{"zzzz", "yyyy"})
|
||||
if detail2 == nil {
|
||||
t.Fatal("expected non-nil result even for non-existent subpath")
|
||||
}
|
||||
matches2, _ := detail2["totalMatches"].(int)
|
||||
if matches2 != 0 {
|
||||
t.Errorf("totalMatches for non-existent subpath = %d, want 0", matches2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubpathDetailMixedCaseHops(t *testing.T) {
|
||||
db := setupRichTestDB(t)
|
||||
defer db.Close()
|
||||
store := NewPacketStore(db, nil)
|
||||
store.Load()
|
||||
|
||||
// Query with lowercase hops to establish baseline
|
||||
lower := store.GetSubpathDetail([]string{"eeff", "0011"})
|
||||
if lower == nil {
|
||||
t.Fatal("expected non-nil detail for lowercase subpath")
|
||||
}
|
||||
lowerMatches, _ := lower["totalMatches"].(int)
|
||||
if lowerMatches == 0 {
|
||||
t.Fatal("expected >0 matches for lowercase subpath")
|
||||
}
|
||||
|
||||
// Query with mixed-case hops — must return the same results (case-insensitive)
|
||||
mixed := store.GetSubpathDetail([]string{"EEFF", "0011"})
|
||||
if mixed == nil {
|
||||
t.Fatal("expected non-nil detail for mixed-case subpath")
|
||||
}
|
||||
mixedMatches, _ := mixed["totalMatches"].(int)
|
||||
if mixedMatches != lowerMatches {
|
||||
t.Errorf("mixed-case totalMatches = %d, want %d (same as lowercase)", mixedMatches, lowerMatches)
|
||||
}
|
||||
|
||||
// All-uppercase should also match
|
||||
upper := store.GetSubpathDetail([]string{"EEFF", "0011"})
|
||||
upperMatches, _ := upper["totalMatches"].(int)
|
||||
if upperMatches != lowerMatches {
|
||||
t.Errorf("uppercase totalMatches = %d, want %d", upperMatches, lowerMatches)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreGetAnalyticsRFCacheHit(t *testing.T) {
|
||||
db := setupRichTestDB(t)
|
||||
defer db.Close()
|
||||
|
||||
Reference in new issue
Block a user