2 Commits
Author SHA1 Message Date
sneak 515bc1e5a2 Exit with status 1 when the web server cannot listen (closes #73)
check / check (push) Canceled after 0s
Server.Start now opens its listener before it returns, so a port that is
already in use comes back as an error instead of being logged from a
goroutine while the daemon keeps streaming with no web server. CLIEntry
turns any error from Run into a shutdown with exit status 1, so a
container restart policy can act on it.

Model: opus-5-5
2026-10-14 14:43:58 +00:00
clawbot a43935308e Count every reconnect in reconnect_count (closes #68)
check / check (push) Canceled after 0s
`reconnect_count` in `/api/v1/stats` stayed 0 however often the stream reconnected: `SetConnected` counted a reconnect only while the stream was still marked connected, and the streamer always marks it disconnected first. It now counts one reconnect for every connection after the first, recognised by the zero `connectedSince` before the first connection. A test connects and disconnects twice and expects 2.

Model: opus-5-5
2026-10-14 15:59:15 +02:00
3 changed files with 36 additions and 14 deletions
-4
View File
@@ -27,10 +27,6 @@ real 5 GiB limit (https://git.eeqj.de/sneak/routewatch/issues/3).
# Completed Steps
- 2026-10-14: the daemon exits with status 1 when it cannot listen on its
port, for example because the port is in use, instead of running on without
its web server; any other error from `Run` also stops it with status 1
(closes #73)
- 2026-10-03: `/api/v1/stats` serves the prefix distribution from memory,
seeded at startup and adjusted on every live-route write, so a request no
longer reads every live route (closes #30)
+15 -10
View File
@@ -60,17 +60,22 @@ func New() *Tracker {
}
}
// SetConnected updates the connection status
// SetConnected updates the connection status. Every connection after the
// first counts one reconnect.
func (t *Tracker) SetConnected(connected bool) {
wasConnected := t.isConnected.Swap(connected)
if connected {
t.mu.Lock()
t.connectedSince = time.Now()
t.mu.Unlock()
// Increment reconnect count (but not for the initial connection)
if wasConnected || t.reconnectCount.Load() > 0 {
t.reconnectCount.Add(1)
}
t.isConnected.Store(connected)
if !connected {
return
}
t.mu.Lock()
// connectedSince is zero until the first connection.
connectedBefore := !t.connectedSince.IsZero()
t.connectedSince = time.Now()
t.mu.Unlock()
if connectedBefore {
t.reconnectCount.Add(1)
}
}
+21
View File
@@ -0,0 +1,21 @@
package metrics
import "testing"
// TestReconnectCount connects three times with a disconnect between each, the
// way the streamer does, and expects every connection after the first to count
// one reconnect.
func TestReconnectCount(t *testing.T) {
tracker := New()
tracker.SetConnected(true)
tracker.SetConnected(false)
tracker.SetConnected(true)
tracker.SetConnected(false)
tracker.SetConnected(true)
const wantReconnects = 2
if got := tracker.GetReconnectCount(); got != wantReconnects {
t.Fatalf("reconnect count = %d, want %d", got, wantReconnects)
}
}