eleven / messenger Public

router: registry watcher polls on a fresh connection each tick (#769)

The shared long-lived handle went blind to cross-process commits in
production — first PRAGMA data_version never advanced (#767), then even
the fingerprint SELECT read a stale snapshot in one process lifetime
while an identical process later saw changes fine. A brand-new
connection per poll reads the latest committed state by construction;
opening SQLite costs ~1ms, nothing at a 2s cadence. Also arms loudly
and logs poll errors (deduped) instead of swallowing them.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Paul Campbell 5d15b68b086c02e0f1c5f6d85a30c867ff9a4ae7 parent 36321a7
1 file changed, +27 −3
  • router.go +27 −3
diff --git a/router.go b/router.go
index 8b7ae69..98c4abd 100644
--- a/router.go
+++ b/router.go
@@ -3,6 +3,7 @@ package main
import (
"context"
"crypto/tls"
+ "database/sql"
"errors"
"flag"
"fmt"
@@ -322,15 +323,38 @@ func runRouter(args []string) {
// moves on any insert, delete, or status/socket change; scanning a few
// dozen rows every two seconds is noise.
go func() {
+ log.Printf("router: registry watcher armed (2s fingerprint poll)")
+ // Each poll opens a FRESH connection: a brand-new connection reads the
+ // latest committed state by construction, immune to whatever pins a
+ // long-lived handle to a stale WAL snapshot (observed in production:
+ // the shared handle went blind to cross-process commits — first with
+ // PRAGMA data_version, then intermittently with plain SELECTs — while
+ // separate processes saw fresh rows throughout). Opening SQLite costs
+ // about a millisecond; at one poll every two seconds that is nothing.
+ poll := func() (string, error) {
+ db, err := sql.Open("sqlite", *registryPath)
+ if err != nil {
+ return "", err
+ }
+ defer db.Close()
+ var fp string
+ err = db.QueryRow(`SELECT COUNT(*) || ':' || COALESCE(MAX(rowid),0) || ':' ||
+ COALESCE(SUM(rowid*17 + LENGTH(status)*3 + LENGTH(socket)),0) FROM routes`).Scan(&fp)
+ return fp, err
+ }
var last string
first := true
+ errs := 0
for range time.Tick(2 * time.Second) {
- var fp string
- err := reg.sql.QueryRow(`SELECT COUNT(*) || ':' || COALESCE(MAX(rowid),0) || ':' ||
- COALESCE(SUM(rowid*17 + LENGTH(status)*3 + LENGTH(socket)),0) FROM routes`).Scan(&fp)
+ fp, err := poll()
if err != nil {
+ errs++
+ if errs == 1 || errs%300 == 0 {
+ log.Printf("router: registry-watch poll error (#%d): %v", errs, err)
+ }
continue
}
+ errs = 0
if first {
last, first = fp, false
continue