node/routing: Enforce routingMaxSize

`limits.routingMaxSize` is documented as "Number of routing table entries
before we start pruning", but the implementation diverges from this.

    DELETE FROM routing WHERE node <> ?1 AND rowid IN
      (SELECT rowid FROM routing WHERE timestamp < ?2 ORDER BY timestamp LIMIT ?3)

`?2` is `now - routingMaxAge`. So if nothing is older than routingMaxAge,
nothing is deleted, however far over routingMaxSize the table has grown. On a
seed following a busy network, entries are refreshed by gossip faster than
they age out, and the cap is never reachable.

For example, on a permissive node with routingMaxSize=1000, routingMaxAge=7d,
I observed:

    routing rows                             94,254
    older than routingMaxAge, ie. prunable       16

Dropping routingMaxAge to 1 day cleared the backlog the 7-day setting had
accumulated -- from 94,254 down to 82,400 rows -- but then stopped, with
8 rows prunable and the table still ... 82x over the cap. The `routing` table
size is therefore decided by arrival rate; the configured max size has no
effect on it.

Split the two evictions so each does what its name says:

- size eviction removes the oldest entries down to `routingMaxSize`,
  regardless of age;
- age eviction then removes anything still older than
`routingMaxAge`.

Size runs first: it takes the oldest rows, so the age pass only fires when
the table was near its limit.
This commit is contained in:
Yorgos Saslis
2026-10-08 12:06:06 +02:00
committed by Lorenz Leutgeb
parent 3129de3b95
commit 73ad1de2a4
+23 -10
View File
@@ -221,20 +221,33 @@ impl Store for Database {
limit: Option<usize>,
ignore: &NodeId,
) -> Result<usize, Error> {
let limit: i64 = limit
.and_then(|limit| i64::try_from(limit).ok())
.unwrap_or(i64::MAX);
let mut stmt = self.db.prepare(
"DELETE FROM routing
WHERE node <> ?1 AND rowid IN
(SELECT rowid FROM routing WHERE timestamp < ?2 ORDER BY timestamp LIMIT ?3)",
)?;
let mut pruned = 0;
if let Some(limit) = limit {
let limit = i64::try_from(limit).unwrap_or(i64::MAX);
let mut stmt = self.db.prepare(
"DELETE FROM routing
WHERE node <> ?1 AND rowid IN
(SELECT rowid FROM routing
WHERE node <> ?1
ORDER BY timestamp ASC
LIMIT ?2)",
)?;
stmt.bind((1, ignore))?;
stmt.bind((2, limit))?;
stmt.next()?;
pruned += self.db.change_count();
}
let mut stmt = self
.db
.prepare("DELETE FROM routing WHERE node <> ?1 AND timestamp < ?2")?;
stmt.bind((1, ignore))?;
stmt.bind((2, &oldest))?;
stmt.bind((3, limit))?;
stmt.next()?;
pruned += self.db.change_count();
Ok(self.db.change_count())
Ok(pruned)
}
fn count(&self, id: &RepoId) -> Result<usize, Error> {