Skip to content

P1: cap the path table at 150 records, and pick up the stack fixes - #2

Open
A13xB0 wants to merge 3 commits into
masterfrom
fix/p1-pool-140k
Open

A13xB0 wants to merge 3 commits into
masterfrom
fix/p1-pool-140k

Conversation

@A13xB0

@A13xB0 A13xB0 commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

The solar P1 ran out of memory after 30 hours: ContainerAllocator failed to allocate memory (64 bytes), with 30 KB nominally free — fragmentation, not exhaustion. Three things were behind it, found from the node's own Heap Pool Stats over a 30-hour soak plus an audit of the stack's containers.

The path table was sized for flash, not RAM

The P1's QSPI branch set path_table_maxsize(500), copied from the RAK external-flash modules. But the path store's index lives in RAM: every record costs a FileStore index entry plus a _path_states entry, about 140 bytes together. Measured on the node, the table sat at 446–479 records — roughly 65 KB of a 140 KB pool, which accounted for the entire 104 KB baseline it booted with.

At 150 records the same node boots at 61% pool used with 46 KB free, against 73% and 22 KB before. The store is LRU, so the paths in use are the ones it keeps.

The neighbour scan ran every jobs() tick (ScotMesh/microReticulum a58ef77)

_scan_neighbor_stats() walked the whole stats map on every tick — four times a second — allocating a candidate vector and a hex string per neighbour per pass. In one hour of the soak: 7,082 scans and 13,166 skip traces. The per-neighbour rate limits bound the probe traffic, not the scan. Nothing it inspects can change faster than the 300-second suspicion and probe windows, so it is now gated at 15 seconds like every other periodic job in that loop.

cull_announce_table stopped on the wrong limit (same commit)

It triggers on _announce_table_maxsize but broke on _path_table_maxsize, so what it trimmed to depended on an unrelated table's size.

Measured

before after
Path table 446–479 150
Pool used 104,444 B (73%) 79,516–87,580 B (55–61%)
Free ~22 KB (15%) ~46 KB (32%)
Live objects 2,104 → 2,395 over 30 h 1,527

Still open

There is no age-based path expiry in this port: the PATHFINDER_E sweep is a comment, DestinationEntry._expires is computed and stored but never read, and the only TTL is a week checked lazily on access. The cap is a ceiling, not a cure — the underlying drift was +307 bytes/hour. _held_announces and _announce_rate_table also have no removal path, though both are dormant on this node.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc

LargeSpark and others added 3 commits September 18, 2026 00:43
The P1 branch set path_table_maxsize(500), copied from the RAK external
flash modules where it was sized for their flash. The path store's index
lives in RAM: each record costs a FileStore index entry plus a
_path_states entry, about 140 bytes. Measured on the solar node, the
table sat at 446-479 records — roughly 65 KB of a 140 KB pool, which was
the whole baseline it booted with before a 64-byte allocation failed 30
hours later. At 150 the same node boots at 61% pool used with 46 KB
free, against 73% and 22 KB before.

Repins microReticulum on a58ef77, which gates the neighbour-stats scan
to an interval instead of running it every jobs() tick, and culls the
announce table to its own limit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJZ75CWob1eMXeTa8Q2nLc
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants